Предисловие


Видеоигры на протяжении уже более 30 лет активно развиваются, начиная от геймплея и графических возможностей, вплоть до продвинутых систем искусственного интеллекта и восприятия пользователями подаваемого разработчиками материала. Киберспорт, в свою очередь, является полноценным видом спорта с многомиллионными призами и миллиардными оборотами. А сама индустрия видеоигр неустанно набирает обороты, являясь одной из наиболее кассовых и массовых ответвлений сферы развлечений. А ко всему этому человечество пришло от примитивнейшей реализации «тенниса», запускавшейся на осциллографе.


Я, как первокурсник, решил в рамках изучения базовых консольных возможностей C/C++ возможно запрограммировать "классическую" ASCII-игру, требующую от игрока скорости мышления, непрерывного взаимодействия с игрой, а также обладающую несложной графической частью, которую игрок мог бы интерпретировать в некое подобие трехмерного изображения.


Несмотря на визуальную примитивность подобных решений, они безусловно вызывают у игроков ностальгию по олдскульным играм.


Вдохновление


На заре появления на рынке мобильных телефонов с монохромными дисплеями выделилась игра «Space Impact», прошивавшаяся в аппаратах Nokia. Она отлично вписалась в ограниченные возможности тогдашних сотовых. После введения простых игр в мобильные телефоны – продажи аппаратов резко возросли, а мобильный гейминг постепенно стал популярным, и сейчас вполне конкурирует со индустрией «взрослых» ААА-проектов.


«Space Impact» и послужила основой для моей первой игры ввиду своей легкости исполнения и популярности в начале 2000-х.


Особенности «Space Invader»


В первую очередь – легковесность программы. Исполняемый файл занимает менее 100 кб и будет работать, как задумано, практически на любом компьютере под OC Windows с пакетом Visual C++.


Во-вторых, переносимость — исходный код можно за несколько минут переделать под POSIX-системы, тем самым обеспечив работоспособность на UNIX и Mac операционных системах, лишь заменив несколько функций и пересобрав программу на соответствующем компиляторе.


Третье преимущество — программа портативна, не требует никаких дополнительных файлов. Все дополнительные файлы при необходимости создаются автоматически в каталоге местонахождения исполняемого файла.


Наипростейший интуитивно понятный интерфейс, не требующий описания или инструкций по использованию.


Содержание игры «Space Invader»
  • Главное меню из 4-х пунктов, выбирать которые можно с помощью стрелок на клавиатуре.
  • Пункт «Игра» — открывает меню их двух пунктов:

    • Подпункт «Новая игра» — запускает новый игровой сеанс.
    • Подпункт «Продолжить» — загружает последнюю сохраненную игру из бинарного файла в директории исполняемого файла и запускает игровой сеанс с использованием полученных данных.

  • Пункт «Помощь» — инструкции по работе с приложением и его описание. Пролистываются по нажатию стрелок на клавиатуре.
  • Пункт «Зал славы» — список лидеров игровых сессий, загружается из бинарного файла в директории исполняемого файла и форматированно выводится в консоль.
  • Пункт «Выход» — выход из приложения.
  • Сдвигающийся влево «мир» с динамической скоростью. «Мир» содержит рандомные поля сверху и снизу толщиной в 1 или 2 символа. Также между полями рандомно появляется «космический мусор», отображаемый символом «¤», являющийся препятствием для игрока.
  • Прижатый к левому краю окна «космический корабль». Корабль смещается с помощью стрелок на клавиатуре вверх и вниз, по нажатию пробела выпускает снаряд, уничтожающий «космический мусор» по касанию.
  • «Приборная панель» вверху консоли, отображающая пройденный километраж, текущую скорость и количество оставшихся попыток.

Как работает «Space Invader»?


При запуске приложения возникает заставка-анимация. Она состоит из 6 заранее отформатированных символьных массивов, сменяющихся через каждые 200мс.


image
Начальная заставка – итоговый слайд


Далее идет обращение в функцию главного меню с параметром 1(целое число). Функция отображает меню с выделенным угловыми скобками пунктом меню, номер которого совпадает с входным параметром. Пунктов в меню 4, соответственно входной параметр может различаться от 1 до 4. При нажатии стрелки вниз происходит рекурсивное обращение с инкрементированным входным параметром в том случае, если входной параметр меньше 4, с параметром 1, если входной параметр равен 4. При нажатии Space или Enter происходит обращение к функции, соответствующей выделенному пункту меню.


void StartMenu(int switcher)
{
    system("cls");
    switch (switcher)
    {
    case 1:
        cout << "\n\n\n                  <<  ИГРАТЬ!  >>\n\n                      ПОМОЩЬ!\n\n                     ЗАЛ СЛАВЫ\n\n                       ВЫХОД";
        break;
    case 2:
        cout << "\n\n\n                      ИГРАТЬ!\n\n                  <<  ПОМОЩЬ!  >>\n\n                     ЗАЛ СЛАВЫ\n\n                       ВЫХОД";
        break;
    case 3:
        cout << "\n\n\n                      ИГРАТЬ!\n\n                      ПОМОЩЬ!\n\n                  << ЗАЛ СЛАВЫ >>\n\n                       ВЫХОД";
        break;
    case 4:
        cout << "\n\n\n                      ИГРАТЬ!\n\n                      ПОМОЩЬ!\n\n                     ЗАЛ СЛАВЫ\n\n                  <<   ВЫХОД   >>";
        break;
    }
    int choice = _getch();
    if (choice == 224)
        choice = _getch();
    if (choice == 72)
        if (switcher != 1)
            StartMenu(switcher - 1);
        else
            StartMenu(4);
    if (choice == 80)
        if (switcher != 4)
            StartMenu(switcher + 1);
        else
            StartMenu(1);
    if (choice == 13 || choice == 32)
    {
        if (switcher == 1)
            GameMenu(1);
        if (switcher == 2)
            Help(0);
        if (switcher == 3)
            TopChart();
        if (switcher == 4)
            _exit(0);
    }
}

image
Главное меню (вход в функцию выполнен с параметром 1)


При обращении к функции, соответствующей пункту «Игра» запускается функция, аналогичная по функционалу, но выбор есть только из 2-х пунктов. Соответственно, входной параметр будет 1 или 2, и при нажатии любой из стрелок (вверх или вниз) нам необходимо лишь сменить цифру на «противоположную». Наиболее оптимизированным будет вариант отнимания входного параметра от 3 (3 – 1 = 2, 3 – 2 = 1).


void GameMenu(int switcher)
{
    system("cls");
    if (switcher == 1)
        cout << "\n\n\n\n\n                <<  НОВАЯ ИГРА!  >>\n\n                    ПРОДОЛЖИТЬ!";
    else
        cout << "\n\n\n\n\n                    НОВАЯ ИГРА!\n\n                <<  ПРОДОЛЖИТЬ!  >>";
    int choice = _getch();
    if (choice == 224)
        choice = _getch();
    if (choice == 72 || choice == 80)
        GameMenu(3 - switcher);
    if (choice == 27)
        StartMenu(1);
    if (choice == 13 || choice == 32)
        Game(switcher);
}

image
Дополнительное меню (вход в функцию выполнен с параметром 1)


Теперь к основному – процессу игры. При выборе подпункта «Новая игра» — запускается новый игровой сеанс. Создается двумерный массив, размерностью 14 строк на 50 столбцов. Первая строка выделяется под приборную панель. Первый прибор – количество пройденных километров, оно равно количеству обновлений консоли (изначально консоль обновляется раз в 80мс, с каждым обновлением этот параметр декрементируется, пока не достигнет значения 25).


int odometerBuf = odometer, odometerDigitLength;
        for (odometerDigitLength = 0; odometerBuf != 0; odometerBuf /= 10, odometerDigitLength++);//вычисление количества цифр на одометре
        for (int i = odometerDigitLength, odometerBuf = odometer; i >= 0; i--, scr[0][i] = odometerBuf % 10 + '0', odometerBuf /= 10);//прорисовка одометра на приборную панель
        scr[0][odometerDigitLength++] = 'К'; scr[0][odometerDigitLength++] = 'М';//дописывание "КМ"
        odometer++;//наращение одометра

Второй прибор, текущая скорость, являет собой формулу — 1000/скорость обновления консоли. Скорость измеряется в километрах в секунду. Таким образом, изначально корабль движется со скоростью 12км/с, и через некоторое время достигает отметки в 40 км/с.


speed = 1000 / timer;//обновление спидометра
        int speedBuf = speed;
        for (int i = 42; speed != 0; i--, scr[0][i] = speed % 10 + '0', speed /= 10);//прорисовка спидометра на приборную панель
        scr[0][42] = 'К'; scr[0][43] = 'М'; scr[0][44] = '/'; scr[0][45] = 'С';//дописывание "КМ/С"

Третий прибор отображает количество оставшихся попыток, изначально их 3. Попытки отображаются символом «&».


for (int i = 50; lifes > 0; i--, lifes--, scr[0][i] = '&');

image
Приборная панель в начале игрового сеанса


Следующие 2 строки, как и последние 2 – являются полями игры. Декорации полей выбираются случайным образом, могут состоять из 3-х символов или пробела. Крайние строки всегда полностью заполнены, а вторая и предпоследняя содержит символы, отличные от пробела лишь в тех местах, где эти символы «растут» из других.


char borderSymbols[] = { '†', '‡', '¤', ' ' };
    for (int aboveBelow = 0; aboveBelow < 50; aboveBelow++)//прорисовка верхнего и нижнего полей (2 + 2)
    {
        scr[1][aboveBelow] = borderSymbols[rand() % 3];
        if (scr[1][aboveBelow] == '‡')
            scr[2][aboveBelow] = '¤';

        scr[13][aboveBelow] = borderSymbols[rand() % 3];
        if (scr[13][aboveBelow] == '‡')
            scr[12][aboveBelow] = '¤';
    }

image
Приборная панель и поля в начале игрового сеанса


В центральной строке игрового пространства, прижатым к левому краю, прорисовывается космический корабль, которым и предстоит управлять игроку.


scr[6][0] = '\\'; scr[6][1] = '\\';//прорисовка корабля
    scr[7][0] = '3'; scr[7][1] = '='; scr[7][2] = '=';
    scr[8][0] = '/'; scr[8][1] = '/';

Управлять ним можно стрелками вверх и вниз.


if (_kbhit())//если клавиша была нажата
        {
            control = _getch();//переменная примет ее значение
            if (control == 224)
                control = _getch();
        }
        if (control == 72)//при движении корабля вверх
            if (scr[2][0] == '\\' || scr[3][0] == '\\' && scr[2][0] == '¤' || scr[3][1] == '\\' && scr[2][1] == '¤')//если корабль врезался в верхнее поле - игра окончена
                if (lifes > 1)
                {
                    cout << '\a';
                    lifes--;
                    weaponPos = 7;
                    GameStart(scr, lifes, &timer);
                    Sleep(1000);
                }
                else
                    GameOver(odometer);
            else
            {
                for (int i = 2; i < 13; i++)//корабль смещается на элемент выше
                    for (int j = 0; j < 49; j++)
                        if (scr[i][j] == '3' || scr[i][j] == '\\' || scr[i][j] == '=' || scr[i][j] == '/')
                        {
                            scr[i - 1][j] = scr[i][j];
                            scr[i][j] = ' ';
                        }
                weaponPos--;
            }
        if (control == 80)//при движении корабля вниз
            if (scr[12][0] == '/' || scr[11][0] == '/' && scr[12][0] == '¤' || scr[11][1] == '/' && scr[12][1] == '¤')//если корабль врезался в нижнее поле - игра окончена
                if (lifes > 1)
                {
                    cout << '\a';
                    lifes--;
                    weaponPos = 7;
                    GameStart(scr, lifes, &timer);
                    Sleep(1000);
                }
                else
                    GameOver(odometer);
            else
            {
                for (int i = 12; i >= 2; i--)//корабль смещается на элемент вниз
                    for (int j = 0; j < 49; j++)
                        if (scr[i][j] == '3' || scr[i][j] == '\\' || scr[i][j] == '=' || scr[i][j] == '/')
                        {
                            scr[i + 1][j] = scr[i][j];
                            scr[i][j] = ' ';
                        }
                weaponPos++;
            }

image
Расположение корабля при двукратном нажатии стрелки вверх


Отойдя от визуализации, стоит отметить, что вместе с кораблем мы смещаем вверх или вниз элемент массива, отвечающий за «дуло» корабля. Соответственно, из которого можно выпускать снаряды по нажатию пробела.


Однако, ни поле, ни снаряды не двигаются, потому, чтобы «оживить» игровой процесс, по истечению таймера перерисовки экрана «мир» будет смещаться на один столбец влево, а снаряды на один столбец вправо. Корабль же остается на месте, пока игрок не захочет иного. Но после перерисовки теперь возникает один пустой столбец – заполним его случайным образом полями и «космическим мусором», который можно сбивать снарядами или боковыми отбойниками корабля.


for (int i = 1; i < 14; i++)//все "космические" элементы смещаются на элемент влево
            for (int j = 0; j < 49; j++)
            {
                if (scr[i][j] == '\\' && scr[i][j + 1] == '¤' || scr[i][j] == '=' && scr[i][j + 1] == '¤' || scr[i][j] == '/' && scr[i][j + 1] == '¤')
                    if (lifes > 1)
                    {
                        cout << '\a';
                        lifes--;
                        weaponPos = 7;
                        GameStart(scr, lifes, &timer);
                        Sleep(1000);
                    }
                    else
                        GameOver(odometer);
                if (scr[i][j] != '3' && scr[i][j] != '\\' && scr[i][j] != '=' && scr[i][j] != '/' && scr[i][j] != '-' && scr[i][j + 1] != '-')
                    scr[i][j] = scr[i][j + 1];
                if (scr[i][j] == '¤')
                    scr[i][j + 1] = ' ';
            }
        for (int i = 1; i < 14; i++)//все снаряды смещаются на элемент вправо
            for (int j = 48; j >= 0; j--)
                if (scr[i][j] == '-')
                    if (j != 48)
                    {
                        scr[i][j + 1] = '-';
                        scr[i][j] = ' ';
                    }
                    else
                        scr[i][j] = ' ';
        char borderSymbols[] = { '†', '‡', '¤', ' ' };
        scr[2][49] = ' ';//рандомное заполнение новых элементов краев
        scr[1][49] = borderSymbols[rand() % 3];
        if (scr[1][49] == '‡')
            scr[2][49] = '¤';
        scr[12][49] = ' ';
        scr[13][49] = borderSymbols[rand() % 3];
        if (scr[13][49] == '‡')
            scr[12][49] = '¤';
        for (int i = 3; i < 12; i++)//рандомное появление космического мусора
        {
            if (rand() % 10 == 1)
                scr[i][49] = '¤';
        }

При движении корабля вверх или вниз в этот момент космический мусор будет уничтожен без урона кораблю, все благодаря отбойникам


image
Если в данный момент не увернуться от мусора стрелкой вниз – корабль будет разбит


Снаряды, выпускаемые кораблем, сконструированы так, что сметают все на пути своего следования. Следовательно, выпустив один снаряд, расчищается коридор в одну строку.


Впрочем, хоть поля состоят из материалов, схожих на космический мусор, их нельзя разрушить отбойниками. Соприкосновение любой части корабля с полями приводит к неминуемому краху. Также, как и попадание космического мусора в лицевую часть корабля. Если в такие моменты на приборной панели есть обозначение еще хотя бы одной «попытки» — игра как будто начинается сначала, но сохранив набранный счет и потеряв одну «попытку». Скорость же сбрасывается до начального значения в 12км/с.


Если же игрок «разбился», а попыток не осталось – игровой сеанс заканчивается, а игроку предлагается ввести его имя, чтобы сохранить свой результат в «зале славы».


image
Предложение игроку ввести имя, чтобы сохранить свой результат в «зале славы»


Приложение обрабатывает 2 файла:


  • «TopChart.bin» — двоичный файл для хранения таблицы лидеров. Данные хранятся в структурах (ник игрока, его счет, дата завершения игрового сеанса). Данные дозаписываются в конец файла при окончании игры. При вызове пункта «Зал славы» файл открывается для чтения с возможностью редактирования. Далее объявляется динамический массив структур, в который переписываются данные из файла, после чего массив сортируется и форматированно выводится в консоль (максимально возможное число результатов – 12, если массив содержит 13 результатов – последний отбрасывается после сортировки). Далее файл перезаписывается массивом структур результатов, после чего массив уничтожается.
  • «CurrentSave.bin» — двоичный файл для хранения сохраненной игры. Вызывается для чтения при запуске подпункта «Продолжить» пункта «Игра». Может содержать данные для восстановления одного незавершенного игрового сеанса: номер строки, в которой содержится нос корабля, количество пройденных километров, количество оставшихся попыток, расположение обьектов на экране. С помощью этих данных формируется игровой сеанс, в точности повторяющий незавершенный. Во избежание нечестной игры, при загрузке сеанса из файла – файл удаляется. При нажатии Escape во время игры данный файл создается, и в него записываются все необходимые данные для успешного дальнейшего продолжения игрового сеанса.

Пункт главного меню «Помощь» — функция, принимающая параметр от 0 до 22, отображающая последующие 12 строк по 50 символов от входного параметра. Управление осуществляется рекурсивно с помощью стрелок вверх и вниз.


void Help(int switcher)
{
    system("cls");
    cout << "ПРОКРУТКА: СТРЕЛКИ ВВЕРХ/ВНИЗ | ВЕРНУТЬСЯ: ESCAPE\n";
    char arr[1800] = { "                УПРАВЛЕНИЕ В МЕНЮ                 Передвигаться по пунктам – СТРЕЛКИ ВВЕРХ/ВНИЗ     Выбрать пункт – ПРОБЕЛ или ENTER                  Вернуться в предыдущее меню – ESCAPE                              УПРАВЛЕНИЕ В ИГРЕ                 Передвигаться вверх/вниз – СТРЕЛКИ ВВЕРХ/ВНИЗ     Сделать выстрел – ПРОБЕЛ                          Вернуться в меню, сохранив игру – ESCAPE                               БРИФИНГ                      Вы – пилот космического корабля, попавшего в      космическую бурю. Вам необходимо не разбиться и   пролететь как можно большее расстояние. Корабль   оборудован динамическим управлением. Чем быстрее  вы летите – тем острее поворачивает судно. Корабльавтоматически постепенно разгоняется до 40 км/с.  Вы можете сбивать космический мусор с помощью     магнитной пушки, встроенной в судно, а также      боковыми отбойниками.                             При управлении кораблем на щитке приборов         отображается пройденная дистанция, текущая        скорость и количество оставшихся «ячеек отката»   (отображаются символом  «&»), изначально их 3.    Если решите прекратить игру – просто нажмите      ESCAPE. Игра сохранится, и вы сможете ее          продолжить даже после перезапуска приложения с    помощью пункта «ПРОДОЛЖИТЬ!».                     В главном меню можно посмотреть таблицу почетных  пилотов. Добейтесь своего права там оказаться!                         АВТОРСТВО                                 Svjatoslav Laskov – AUTHOR                          Igor Marchenko – COACH                         National Technical University                    «Kharkiv Polytechnic Institute»                                                     2016" };
    for (int i = 0, buf = switcher; i < 13; i++)
    {
        for (int j = buf * 50; j < buf * 50 + 50; j++)
            cout << arr[j];
        if (i != 12)
            cout << endl;
        buf++;
    }
    int controller = _getch();//получить значение нажатой клавиши
    if (controller == 224)//если была нажата стрелка
        controller = _getch();//то определить какая именно
    if (controller == 72)//если стрелка вверх
        if (switcher > 0)
            Help(switcher - 1);
        else
            Help(0);
    if (controller == 80)//если стрелка вниз
        if (switcher < 22)
            Help(switcher + 1);
        else
            Help(22);
    if (controller == 27)//если Escape
        StartMenu(2);
}

Пункт главного меню «Выход» — осуществляет выход из приложения.


Руководство пользователя

• УПРАВЛЕНИЕ В МЕНЮ
o Передвигаться по пунктам – СТРЕЛКИ ВВЕРХ/ВНИЗ
o Выбрать пункт – ПРОБЕЛ или ENTER
o Вернуться в предыдущее меню – ESCAPE
• УПРАВЛЕНИЕ В ИГРЕ
o Передвигаться вверх/вниз – СТРЕЛКИ ВВЕРХ/ВНИЗ
o Сделать выстрел – ПРОБЕЛ
o Вернуться в меню, сохранив игру – ESCAPE
• БРИФИНГ
Вы – пилот космического корабля, попавшего в космическую бурю. Вам необходимо не разбиться и пролететь как можно большее расстояние.


Корабль оборудован динамическим управлением. Чем быстрее вы летите – тем острее поворачивает судно. Корабль автоматически постепенно разгоняется до 40 км/с.


Вы можете сбивать космический мусор с помощью магнитной пушки, встроенной в судно, а также боковыми отбойниками.
При управлении кораблем на щитке приборов отображается пройденная дистанция, текущая скорость и количество оставшихся «ячеек отката» (отображаются символом «&»), изначально их 3.


Если решите прекратить игру – просто нажмите ESCAPE. Игра сохранится, и вы сможете ее продолжить даже после перезапуска приложения с помощью пункта «ПРОДОЛЖИТЬ!».


В главном меню можно посмотреть таблицу почетных пилотов. Добейтесь своего права там оказаться!


Итог


Лично я получил уйму удовольствия от разработки столь мелочного проекта, тем более, что стандартная консоль предназначена для вывода информации, и никак не для игр. Из-за этого фреймрейт получился очень ограничен независимо от компьютера, на котором приложение запускается.


Хотелось еще разделить приложение на несколько потоков для более корректной реакции на нажатие клавиш пользователем, но треды я реализую уже в следующем проекте на С++.


Исходный код
#include "conio.h"
#include "windows.h"
#include "ctime"
#include <iostream>
using namespace std;

struct player//определение структуры, хранящей данные о результатах какого-либо завершенного игрового сеанса
{
    char name[7];
    int score;
    int mday;
    int mon;
    int year;
};

struct save//определение структуры, хранящей данные о незавершенном игровом сеансе
{
    int weaponPos;
    int timer;
    int odometer;
    int lifes;
    char scr[14][50];
};

void ScreenOutput(char scr[14][50])//функция поэлементного вывода массива в консоль
{
    system("cls");
    for (int i = 0; i < 14; i++)
    {
        for (int j = 0; j < 50; j++)
            cout << scr[i][j];
        if (i != 13)
            cout << endl;
    }
}
//блок прототипов функций
void StartMenu(int switcher);//функция, вызывающаяся из главного меню, содержит пункты "ИГРА" и "ПРОДОЛЖИТЬ"
void GameMenu(int switcher);//функция главного меню
void GameStart(char scr[14][50], int lifes, int *timer);//функция, определяющая начальный символьный массив при запуске нового игрового сеанса
void Game(int var);//функция игровго сеанса
void GameOver(int score);//функция, спрашивающая имя игрока, и записывающая его результат в бинарный файл
void Help(int switcher);//функция помощи игроку
void TopChart();//функция "ЗАЛ СЛАВЫ" - отображает список лидеров

void Help(int switcher)
{
    system("cls");
    cout << "ПРОКРУТКА: СТРЕЛКИ ВВЕРХ/ВНИЗ | ВЕРНУТЬСЯ: ESCAPE\n";
    char arr[1800] = { "                УПРАВЛЕНИЕ В МЕНЮ                 Передвигаться по пунктам – СТРЕЛКИ ВВЕРХ/ВНИЗ     Выбрать пункт – ПРОБЕЛ или ENTER                  Вернуться в предыдущее меню – ESCAPE                              УПРАВЛЕНИЕ В ИГРЕ                 Передвигаться вверх/вниз – СТРЕЛКИ ВВЕРХ/ВНИЗ     Сделать выстрел – ПРОБЕЛ                          Вернуться в меню, сохранив игру – ESCAPE                               БРИФИНГ                      Вы – пилот космического корабля, попавшего в      космическую бурю. Вам необходимо не разбиться и   пролететь как можно большее расстояние. Корабль   оборудован динамическим управлением. Чем быстрее  вы летите – тем острее поворачивает судно. Корабльавтоматически постепенно разгоняется до 40 км/с.  Вы можете сбивать космический мусор с помощью     магнитной пушки, встроенной в судно, а также      боковыми отбойниками.                             При управлении кораблем на щитке приборов         отображается пройденная дистанция, текущая        скорость и количество оставшихся «ячеек отката»   (отображаются символом  «&»), изначально их 3.    Если решите прекратить игру – просто нажмите      ESCAPE. Игра сохранится, и вы сможете ее          продолжить даже после перезапуска приложения с    помощью пункта «ПРОДОЛЖИТЬ!».                     В главном меню можно посмотреть таблицу почетных  пилотов. Добейтесь своего права там оказаться!                         АВТОРСТВО                                 Svjatoslav Laskov – AUTHOR                          Igor Marchenko – COACH                         National Technical University                    «Kharkiv Polytechnic Institute»                                                     2016" };
    for (int i = 0, buf = switcher; i < 13; i++)
    {
        for (int j = buf * 50; j < buf * 50 + 50; j++)
            cout << arr[j];
        if (i != 12)
            cout << endl;
        buf++;
    }
    int controller = _getch();//получить значение надатой клавиши
    if (controller == 224)//если была нажата стрелка
        controller = _getch();//то определить какая именно
    if (controller == 72)//если стрелка вверх
        if (switcher > 0)
            Help(switcher - 1);
        else
            Help(0);
    if (controller == 80)//если стрелка вниз
        if (switcher < 22)
            Help(switcher + 1);
        else
            Help(22);
    if (controller == 27)//если Escape
        StartMenu(2);
}

void StartMenu(int switcher)
{
    system("cls");
    switch (switcher)
    {
    case 1:
        cout << "\n\n\n                  <<  ИГРАТЬ!  >>\n\n                      ПОМОЩЬ!\n\n                     ЗАЛ СЛАВЫ\n\n                       ВЫХОД";
        break;
    case 2:
        cout << "\n\n\n                      ИГРАТЬ!\n\n                  <<  ПОМОЩЬ!  >>\n\n                     ЗАЛ СЛАВЫ\n\n                       ВЫХОД";
        break;
    case 3:
        cout << "\n\n\n                      ИГРАТЬ!\n\n                      ПОМОЩЬ!\n\n                  << ЗАЛ СЛАВЫ >>\n\n                       ВЫХОД";
        break;
    case 4:
        cout << "\n\n\n                      ИГРАТЬ!\n\n                      ПОМОЩЬ!\n\n                     ЗАЛ СЛАВЫ\n\n                  <<   ВЫХОД   >>";
        break;
    }
    int choice = _getch();
    if (choice == 224)
        choice = _getch();
    if (choice == 72)
        if (switcher != 1)
            StartMenu(switcher - 1);
        else
            StartMenu(4);
    if (choice == 80)
        if (switcher != 4)
            StartMenu(switcher + 1);
        else
            StartMenu(1);
    if (choice == 13 || choice == 32)
    {
        if (switcher == 1)
            GameMenu(1);
        if (switcher == 2)
            Help(0);
        if (switcher == 3)
            TopChart();
        if (switcher == 4)
            _exit(0);
    }
}

void GameMenu(int switcher)
{
    system("cls");
    if (switcher == 1)
        cout << "\n\n\n\n\n                <<  НОВАЯ ИГРА!  >>\n\n                    ПРОДОЛЖИТЬ!";
    else
        cout << "\n\n\n\n\n                    НОВАЯ ИГРА!\n\n                <<  ПРОДОЛЖИТЬ!  >>";
    int choice = _getch();
    if (choice == 224)
        choice = _getch();
    if (choice == 72 || choice == 80)
        GameMenu(3 - switcher);
    if (choice == 27)
        StartMenu(1);
    if (choice == 13 || choice == 32)
        Game(switcher);
}

void GameStart(char scr[14][50], int lifes, int *timer)
{
    for (int i = 0; i < 14; i++)//очищение от мусора
        for (int j = 0; j < 50; j++)
            scr[i][j] = ' ';
    for (int i = 50; lifes > 0; i--, lifes--, scr[0][i] = '&');
    *timer = 80;
    char borderSymbols[] = { '†', '‡', '¤', ' ' };
    for (int aboveBelow = 0; aboveBelow < 50; aboveBelow++)//прорисовка верхнего и нижнего полей (2 + 2)
    {
        scr[1][aboveBelow] = borderSymbols[rand() % 3];
        if (scr[1][aboveBelow] == '‡')
            scr[2][aboveBelow] = '¤';

        scr[13][aboveBelow] = borderSymbols[rand() % 3];
        if (scr[13][aboveBelow] == '‡')
            scr[12][aboveBelow] = '¤';
    }
    scr[6][0] = '\\'; scr[6][1] = '\\';//прорисовка корабля
    scr[7][0] = '3'; scr[7][1] = '='; scr[7][2] = '=';
    scr[8][0] = '/'; scr[8][1] = '/';
}

void GameOver(int score)
{
    system("cls");
    player newPlayer;//объявляние структуры
    newPlayer.score = score;//инициализацие поля набранного счета
    cout << "Поздравляем Вас!\nВы продержались " << score << " километров.\n\n(Пожалуйста, не используйте кириллические символы)\n(Используйте не более 6 символов)\nОставьте свое имя и станьте примером\nдля подражания будущим игрокам: ";
    cin.getline(newPlayer.name, 7);//инициализацие поля имени
    time_t timeCur;
    time(&timeCur);
    struct tm * timeCurStruct = localtime(&timeCur);
    newPlayer.mday = timeCurStruct->tm_mday;//инициализацие даты завершения игры
    newPlayer.mon = timeCurStruct->tm_mon;
    newPlayer.year = timeCurStruct->tm_year;
    FILE *topChart;
    fopen_s(&topChart, "TopChart.bin", "ab+");
    fwrite(&newPlayer, 1, sizeof(player), topChart);//дозапись результата в файл
    fclose(topChart);
    TopChart();
}

void TopChart()
{
    FILE *topChart;
    fopen_s(&topChart, "TopChart.bin", "rb+");
    system("cls");
    if (topChart == NULL)//если произошла ошибка при открытии файла
    {
        system("cls");
        cout << "Нет ни единого результата.";
        Sleep(1000);
        system("cls");
        cout << "Нет ни единого результата..";
        Sleep(1000);
        system("cls");
        cout << "Нет ни единого результата...";
        Sleep(1000);
        cout << "\nНажмите любую клавишу, чтобы вернуться.";
        _getch();
        StartMenu(3);
    }
    fseek(topChart, 0L, SEEK_END);
    int playerAmount = ftell(topChart) / sizeof(player);
    player *temp = new player[playerAmount];
    fseek(topChart, 0L, SEEK_SET);
    for (int i = 0; i < playerAmount; i++)//копирование содержиомого файла в структкры
        fread(&temp[i], 1, sizeof(player), topChart);
    fclose(topChart);
    for (int i = 1; i < playerAmount; i++)//сортировка структур по спаданию итоговых счетов
        if (temp[i].score > temp[i - 1].score)
        {
            player tempAlone;
            strcpy(tempAlone.name, temp[i].name);
            tempAlone.score = temp[i].score;
            tempAlone.mday = temp[i].mday;
            tempAlone.mon = temp[i].mon;
            tempAlone.year = temp[i].year;

            strcpy(temp[i].name, temp[i - 1].name);
            temp[i].score = temp[i - 1].score;
            temp[i].mday = temp[i - 1].mday;
            temp[i].mon = temp[i - 1].mon;
            temp[i].year = temp[i - 1].year;

            strcpy(temp[i - 1].name, tempAlone.name);
            temp[i - 1].score = tempAlone.score;
            temp[i - 1].mday = tempAlone.mday;
            temp[i - 1].mon = tempAlone.mon;
            temp[i - 1].year = tempAlone.year;

            if (i > 1)
                i -= 2;
            else
                i = 0;
        }
    if (playerAmount > 12)
        playerAmount = 12;
    cout << "№       " << "Имя" << '\t' << "Счет" << '\t' << "Дата" << endl;//вывод таблицы лидеров в консоль
    for (int i = 0; i < playerAmount; i++)
    {
        cout << i + 1 << ')' << '\t' << temp[i].name << '\t' << temp[i].score << '\t';
        if (temp[i].mday / 10 == 0)
            cout << '0' << temp[i].mday;
        else
            cout << temp[i].mday;
        cout << ' ';
        switch (temp[i].mon)
        {
        case 0:
            cout << "января";
            break;
        case 1:
            cout << "февраля";
            break;
        case 2:
            cout << "марта";
            break;
        case 3:
            cout << "апреля";
            break;
        case 4:
            cout << "мая";
            break;
        case 5:
            cout << "июня";
            break;
        case 6:
            cout << "июля";
            break;
        case 7:
            cout << "августа";
            break;
        case 8:
            cout << "сентября";
            break;
        case 9:
            cout << "октября";
            break;
        case 10:
            cout << "ноября";
            break;
        case 11:
            cout << "декабря";
            break;
        }
        cout << ' ' << 1900 + temp[i].year << endl;
    }
    fopen_s(&topChart, "TopChart.bin", "wb+");
    for (int i = 0; i < playerAmount; i++)//запись таблицы лидеров в бинарный файл
        fwrite(&temp[i], 1, sizeof(player), topChart);
    fclose(topChart);
    delete[] temp;
    _getch();
    StartMenu(3);
}

int main()
{
    setlocale(LC_ALL, "Rus");//задание кодировки
    system("mode con cols=51 lines=14");//задание размеров окна консоли
    system("title Space Invader");//задание описания окна консоли
    system("color 0A");//задание цвета консоли (0-задний фон; А-передний фон)
    HANDLE hCons = GetStdHandle(STD_OUTPUT_HANDLE);//получение хендла
    CONSOLE_CURSOR_INFO cursor = { 100, false };//число от 1 до 100 размер курсора в процентах; false\true - видимость
    SetConsoleCursorInfo(hCons, &cursor);//применение заданных параметров курсора
    int timer = 200;
    cout << "          (____/(__)  \\_/\\_/ \\___)(____)\n\n\n\n\n\n\n\n\n\n\n      __   __ _  _  _   __   ____  ____  ____\n     (  )(  ( \\/ )( \\ / _\\ (    \\(  __)(  _ \\";//вступительная заставка
    Sleep(timer);
    system("cls");
    cout << "          \\___ \\ ) __//    \\( (__  ) _)\n          (____/(__)  \\_/\\_/ \\___)(____)\n\n\n\n\n\n\n\n\n      __  __ _  _  _   __   ____  ____  ____\n     (  )(  ( \\/ )( \\ / _\\ (    \\(  __)(  _ \\\n      )( /    /\\ \\/ //    \\ ) D ( ) _)  )   /";//вступительная заставка
    Sleep(timer);
    system("cls");
    cout << "          / ___)(  _ \\ / _\\  / __)(  __)\n          \\___ \\ ) __//    \\( (__  ) _)\n          (____/(__)  \\_/\\_/ \\___)(____)\n\n\n\n\n\n\n      __  __ _  _  _   __   ____  ____  ____\n     (  )(  ( \\/ )( \\ / _\\ (    \\(  __)(  _ \\\n      )( /    /\\ \\/ //    \\ ) D ( ) _)  )   /\n     (__)\\_)__) \\__/ \\_/\\_/(____/(____)(__\\_)";//вступительная заставка
    Sleep(timer);
    system("cls");
    cout << "           ____  ____   __    ___  ____\n          / ___)(  _ \\ / _\\  / __)(  __)\n          \\___ \\ ) __//    \\( (__  ) _)\n          (____/(__)  \\_/\\_/ \\___)(____)\n\n\n\n\n      __  __ _  _  _   __   ____  ____  ____\n     (  )(  ( \\/ )( \\ / _\\ (    \\(  __)(  _ \\\n      )( /    /\\ \\/ //    \\ ) D ( ) _)  )   /\n     (__)\\_)__) \\__/ \\_/\\_/(____/(____)(__\\_)";//вступительная заставка
    Sleep(timer);
    system("cls");
    cout << "\n           ____  ____   __    ___  ____\n          / ___)(  _ \\ / _\\  / __)(  __)\n          \\___ \\ ) __//    \\( (__  ) _)\n          (____/(__)  \\_/\\_/ \\___)(____)\n\n\n      __  __ _  _  _   __   ____  ____  ____\n     (  )(  ( \\/ )( \\ / _\\ (    \\(  __)(  _ \\\n      )( /    /\\ \\/ //    \\ ) D ( ) _)  )   /\n     (__)\\_)__) \\__/ \\_/\\_/(____/(____)(__\\_)";//вступительная заставка
    Sleep(timer);
    system("cls");
    cout << "\n\n           ____  ____   __    ___  ____\n          / ___)(  _ \\ / _\\  / __)(  __)\n          \\___ \\ ) __//    \\( (__  ) _)\n          (____/(__)  \\_/\\_/ \\___)(____)\n      __  __ _  _  _   __   ____  ____  ____\n     (  )(  ( \\/ )( \\ / _\\ (    \\(  __)(  _ \\\n      )( /    /\\ \\/ //    \\ ) D ( ) _)  )   /\n     (__)\\_)__) \\__/ \\_/\\_/(____/(____)(__\\_)";//вступительная заставка
    cout << '\a';
    Sleep(10 * timer);//задержка заставки
    StartMenu(1);
    return 0;
}

void Game(int var)
{
    int weaponPos;//позиция строки дула в массиве
    int timer;//задержка между перерисовками экрана
    int odometer;//количество перерисовок экрана, они же итоговые очки
    int lifes;//количество жизней
    char control = '&';//переменная управления кораблем
    int shotPause = 4;//задержка между выстрелами (указывать на одну перерисовку больше)
    int speed;//скорость корабля
    char scr[14][50];
    if (var == 1)
    {
        weaponPos = 7;//позиция строки дула в массиве
        odometer = 1;//количество перерисовок экрана, они же итоговые очки
        lifes = 3;//количество жизней
        GameStart(scr, lifes, &timer);
    }
    else//при восстановлении игрового сеанса из сохранения
    {
        FILE *saveBin;
        fopen_s(&saveBin, "CurrentSave.bin", "rb");
        if (!saveBin)
        {
            system("cls");
            cout << "Нет сохранения.";
            Sleep(1000);
            system("cls");
            cout << "Нет сохранения..";
            Sleep(1000);
            system("cls");
            cout << "Нет сохранения...";
            Sleep(1000);
            Game(1);
        }
        fread(&weaponPos, 1, sizeof(int), saveBin);
        timer = 80;
        fread(&odometer, 1, sizeof(int), saveBin);
        fread(&lifes, 1, sizeof(int), saveBin);
        fread(&scr, 14 * 50, sizeof(char), saveBin);
        fclose(saveBin);
        remove("CurrentSave.bin");
    }
    while (true)
    {
        int odometerBuf = odometer, odometerDigitLength;
        for (odometerDigitLength = 0; odometerBuf != 0; odometerBuf /= 10, odometerDigitLength++);//вычисление количества цифр на одометре
        for (int i = odometerDigitLength, odometerBuf = odometer; i >= 0; i--, scr[0][i] = odometerBuf % 10 + '0', odometerBuf /= 10);//прорисовка одометра на приборную панель
        scr[0][odometerDigitLength++] = 'К'; scr[0][odometerDigitLength++] = 'М';//дописывание "КМ"
        odometer++;//наращение одометра
        speed = 1000 / timer;//обновление спидометра
        int speedBuf = speed;
        for (int i = 42; speed != 0; i--, scr[0][i] = speed % 10 + '0', speed /= 10);//прорисовка спидометра на приборную панель
        scr[0][42] = 'К'; scr[0][43] = 'М'; scr[0][44] = '/'; scr[0][45] = 'С';//дописывание "КМ/С"
        if (_kbhit())//если клавиша была нажата
        {
            control = _getch();//переменная примет ее значение
            if (control == 224)
                control = _getch();
        }
        if (control == 13 && shotPause == 4 || control == 32 && shotPause == 4)//при нажатии на курок если пушка перезаряжена
        {
            scr[weaponPos][3] = '-';
            shotPause = 0;
        }
        if (shotPause < 4)//перезарядка
            shotPause++;
        if (control == 27)//при выходе
        {
            FILE *saveBin;
            fopen_s(&saveBin, "CurrentSave.bin", "wb");
            fwrite(&weaponPos, 1, sizeof(int), saveBin);
            fwrite(&odometer, 1, sizeof(int), saveBin);
            fwrite(&lifes, 1, sizeof(int), saveBin);
            fwrite(&scr, 14 * 50, sizeof(char), saveBin);
            fclose(saveBin);
            GameMenu(2);
        }
        if (control == 72)//при движении корабля вверх
            if (scr[2][0] == '\\' || scr[3][0] == '\\' && scr[2][0] == '¤' || scr[3][1] == '\\' && scr[2][1] == '¤')//если корабль врезался в верхнее поле - игра окончена
                if (lifes > 1)
                {
                    cout << '\a';
                    lifes--;
                    weaponPos = 7;
                    GameStart(scr, lifes, &timer);
                    Sleep(1000);
                }
                else
                    GameOver(odometer);
            else
            {
                for (int i = 2; i < 13; i++)//корабль смещается на элемент выше
                    for (int j = 0; j < 49; j++)
                        if (scr[i][j] == '3' || scr[i][j] == '\\' || scr[i][j] == '=' || scr[i][j] == '/')
                        {
                            scr[i - 1][j] = scr[i][j];
                            scr[i][j] = ' ';
                        }
                weaponPos--;
            }
        if (control == 80)//при движении корабля вниз
            if (scr[12][0] == '/' || scr[11][0] == '/' && scr[12][0] == '¤' || scr[11][1] == '/' && scr[12][1] == '¤')//если корабль врезался в нижнее поле - игра окончена
                if (lifes > 1)
                {
                    cout << '\a';
                    lifes--;
                    weaponPos = 7;
                    GameStart(scr, lifes, &timer);
                    Sleep(1000);
                }
                else
                    GameOver(odometer);
            else
            {
                for (int i = 12; i >= 2; i--)//корабль смещается на элемент вниз
                    for (int j = 0; j < 49; j++)
                        if (scr[i][j] == '3' || scr[i][j] == '\\' || scr[i][j] == '=' || scr[i][j] == '/')
                        {
                            scr[i + 1][j] = scr[i][j];
                            scr[i][j] = ' ';
                        }
                weaponPos++;
            }
        for (int i = 1; i < 14; i++)//все "космические" элементы смещаются на элемент влево
            for (int j = 0; j < 49; j++)
            {
                if (scr[i][j] == '\\' && scr[i][j + 1] == '¤' || scr[i][j] == '=' && scr[i][j + 1] == '¤' || scr[i][j] == '/' && scr[i][j + 1] == '¤')
                    if (lifes > 1)
                    {
                        cout << '\a';
                        lifes--;
                        weaponPos = 7;
                        GameStart(scr, lifes, &timer);
                        Sleep(1000);
                    }
                    else
                        GameOver(odometer);
                if (scr[i][j] != '3' && scr[i][j] != '\\' && scr[i][j] != '=' && scr[i][j] != '/' && scr[i][j] != '-' && scr[i][j + 1] != '-')
                    scr[i][j] = scr[i][j + 1];
                if (scr[i][j] == '¤')
                    scr[i][j + 1] = ' ';
            }
        for (int i = 1; i < 14; i++)//все снаряды смещаются на элемент вправо
            for (int j = 48; j >= 0; j--)
                if (scr[i][j] == '-')
                    if (j != 48)
                    {
                        scr[i][j + 1] = '-';
                        scr[i][j] = ' ';
                    }
                    else
                        scr[i][j] = ' ';
        char borderSymbols[] = { '†', '‡', '¤', ' ' };
        scr[2][49] = ' ';//рандомное заполнение новых элементов краев
        scr[1][49] = borderSymbols[rand() % 3];
        if (scr[1][49] == '‡')
            scr[2][49] = '¤';
        scr[12][49] = ' ';
        scr[13][49] = borderSymbols[rand() % 3];
        if (scr[13][49] == '‡')
            scr[12][49] = '¤';
        for (int i = 3; i < 12; i++)//рандомное появление космического мусора
        {
            if (rand() % 10 == 1)
                scr[i][49] = '¤';
        }
        ScreenOutput(scr);//вывод экрана
        if (control != '&')//"обнуление" управляющей переменной
            control = '&';
        if (timer > 25)//ускорение корабля
            timer--;
        Sleep(timer);//задержка перерисовки
    }
}

Загрузить исполняемый файл .exe можно тут: Space Invader.


P.S.: Это мой первый проект и статья на Хабре, жду комментариев.

Поделиться с друзьями
-->

Комментарии (140)


  1. vlreshet
    30.06.2016 11:21
    -4

    Такое ощущение что курсовую прочитал. Мой тебе совет — убирай в черновики пока карма в минус не улетела.


    1. simpleadmin
      30.06.2016 11:56
      +14

      Человек свою курсовую не побоялся на всеобщий срач выставить. Уже это прохвально.
      А что касается качества и формата статьи, то пусть и не совсем формат хабра, но в последнее время в корпоративных блогах куда хуже статьи.


      1. vlreshet
        30.06.2016 12:00
        -4

        Корпоративные статьи на то и корпоративные чтобы писать в них что захотят, ибо оплачено. Но пост с курсовой работой от первокурсника — это слишком, я считаю. Какая польза от такого поста? Кому он будет интересен?

        P.S. Теперь жду пока мне сольют карму дальше. Тут же так принято — минусовать без обоснований.


        1. simpleadmin
          30.06.2016 12:04
          +4

          Теперь жду пока мне сольют карму дальше

          Если камень в мой огород, то мимо :)


        1. GreyCat
          30.06.2016 12:15
          +24

          Ну, разве что использовать как пособие «как нельзя программировать на C++ ни в коем случае». Я вот всегда признаюсь, что я C++ не знаю и не умею, но даже я представляю, что не стоит «писать на C++», если программа де факто написана на C, не стоит делать using namespace std, не стоит использовать C-подобные конструкции (вроде FILE) там, где есть C++-конструкции.

          В 2016 году, казалось бы, есть auto, есть pass by reference, есть std::unique_ptr и std::shared_ptr, есть еще много чего. Отдельно забавно видеть консольное приложение, заботливо привязанное к Windows и через <windows.h>, и через system()-вызовы всяких консольных утилит cmd.exe, и rand без srand, и memory leaks (new player[] -> но нет delete). Дополнительные зведочки приложение может получить за «запихнем все в один файл», неведомый code style и комментарии на русском языке.


          1. SkyHunter
            30.06.2016 12:21
            +1

            Вы не забывайте, что автор — первокурсник.


            1. vlreshet
              30.06.2016 12:27
              +21

              Тогда опять таки возникает вопрос — зачем это на хабре? Какая польза сообществу?


              1. 1vanK
                30.06.2016 12:29
                -12

                Я был согласен с Вашей точкой зрения, но потом посмотрел, что у вас вообще нет статей.


                1. vlreshet
                  30.06.2016 13:27
                  +18

                  Чтобы сказать что суп говно — не нужно быть поваром. У меня нет статей потому что я не считаю что уже создал что-то такое о чём стоит написать в крупнейшее IT сообщество.


                  1. 1vanK
                    30.06.2016 13:40
                    -5

                    Ну и зачем Вы на Хабре? Какая польза сообществу?


                    1. vlreshet
                      30.06.2016 13:42
                      +11

                      Я — это и есть сообщество. Его часть, если быть точнее. И я не обязан приносить ему пользу. А мои статьи — обязаны. Поэтому их и нет. И вообще, ваш вопрос похож на «сперва добейся».


                      1. 1vanK
                        30.06.2016 13:47
                        -10

                        Конечно, но сообщество подобного рода не может писать в комментариях и критиковать других.


                        1. vlreshet
                          30.06.2016 13:51
                          +5

                          С чего это? По-вашему — комментировать и критиковать могут только те кто сам имеет статьи? Остальные же — должны молча восхвалять?


                          1. 1vanK
                            30.06.2016 13:54
                            -5

                            А что, сейчас любой уже может писать в комментариях? Возможно я отстал от жизни.


                            1. Charg
                              30.06.2016 20:13

                              Давно уже, первых 5 (вроде) комментариев на премодерации, дальше свободно.


                          1. SkyHunter
                            30.06.2016 15:23
                            -4

                            Нет, не должны, но намного лучше делать это примерно вот так, а не как вы. ИМХО.


                            1. inborn_killer
                              30.06.2016 19:38
                              +5

                              А разве что-то чрезмерно обидное было сказано? Кто-то кого-то назвал г… ном, пожелал убиться или что-то такое? По-моему, критика лишь совсем чуточку резкая, но такое можно пережить. Нет, ну правда, вы считаете, что хвалить просто за то, что «осмелился выложить» — это правильно?


                              1. 1vanK
                                30.06.2016 20:19
                                -6

                                Да нет, ничего особо обидного не было. Возможно статья не рассчитана на матерых профессионалов, но для совсем уж новичков она будет полезной (я могу об этом судить хотя бы по числу закладок), а значит свою аудиторию она найдет. И еще какие-то странные аналогии с поварами. Прежде чем повара критиковать, ему заплатить надо. Просто удивляет позиция людей, которые вносят ноль импакта, но берут на себя роль модераторов, рассуждая что должно быть на Хабре, а что нет.


                                1. inborn_killer
                                  30.06.2016 20:39
                                  +5

                                  Для профессионалов она бесполезна, а для новичков — опасна. Понимаете, эта статья — тот самый информационный шум, который мешает новичку найти по-настоящему полезную информацию. По-моему, критика здесь вполне к месту. Бывают просто слабые статьи, где действительно «автор молодец, что постарался, но надо доделать тут и тут», а бывает вот такое. Пусть студент учится и дальше, пусть у него всё хорошо в жизни сложится, но пока что рано писать статьи на хабр. Разве вы не согласны?


                                  1. 1vanK
                                    30.06.2016 20:46
                                    +3

                                    Хорошо, Вы правы.


                                  1. lemelisk
                                    30.06.2016 21:00
                                    +3

                                    При чем, на мой взгляд, косяк тут в основном не автора (попытался и попытался — ничего в этом такого нету, в конце концов, деньги же за это не берут), а выдавшего за эту статью инвайт.


              1. Robotex
                04.07.2016 21:28
                -2

                Другому первокурснику польза


            1. GreyCat
              30.06.2016 12:30
              +11

              Вот это меня больше всего убивает. Это значит, что где-то же так учат делать.

              А главное, как подается — «апогей возможностей». Я, простите, когда в школе в 8-9 классе учился, вокруг наблюдал куда большую культуру программирования.

              Сейчас вот когда беру студентов 1-2 курса на парт-тайм — в массе своей тоже люди куда более адекватные, по-моему, попадаются. По крайней мере не пишут на C++ то, что на нем можно не писать, и вполне бойко ориентируются в каком-нибудь распространенном скриптовом языке (Python, PHP, Ruby, JS), не падают в обморок от известия о том, что в мире есть что-то кроме Windows, не смущаются проектов из больше, чем одного файла и т.д.


              1. Nipheris
                30.06.2016 12:44
                +6

                Классные к вам ребята приходят с первых-вторых курсов). С таким описанием (бойко ориентироваться хотя бы в одном скриптовом языке, не бояться факта существования *nix, не бояться проектов из >1 файла) к нам приходят выпускники, а не первокурсники. Это не лучшие, конечно, выпускники, но тем не менее десятки их. И возможно они даже толковые ребята в перспективе, просто испытывают полнейшие и абсолютное отсутствие практики.


                1. GreyCat
                  30.06.2016 12:53

                  Есть хорошие институты, где за 5-6 лет человеку что-то преподадут и он действительно будет достаточно загружен, чтобы развиваться именно тем и так, как ему предлагается в институте. Но их, разумеется, очень мало.

                  В массе своей в институтах преподают мало и плохо (в том числе фундаментальные вещи, которые по идее должны были бы), в итоге если человек из такого института 6 лет просиживал штаны, не развивался самостоятельно и не имеет практики — это уже индикатор. Человек не хочет развиваться, не имеет каких-то стремлений, и скорее эти 6 лет предпочитал играть в игрушки, пить пиво на лавочке или потратить их еще каким-нибудь подобным неконструктивным образом. Зачем брать на себя роль еще и воспитателя и пытаться это перевоспитать с неясной вероятностью успеха?


                  1. Nipheris
                    01.07.2016 03:40

                    Нет, ну роль воспитателя брать на себя конечно ни к чему, я в принципе удивился возможности брать 1-2 курс на частичную занятость). У нас первые два курса был «букварь» — основы алгоритмов, основы языков программирования, к концу второго курса мы наконец узнали про списки, стеки и очереди. И я бы не сказал, что всё это реально уместить меньше чем на два курса.

                    Кстати, насчёт школы. Я тоже свою первую программу написал еще классе в 8 на QBasic-е, однако в моё время ориентироваться на школьную информатику было бы большой ошибкой. Об обучении программированию не могло быть и речи — информатика ограничивалась переводами чисел в разные системы счисления и оформлениями презентаций в ПаверПоинте. Надеюсь, ситуация изменилась с той поры (хотя, в Москве/Питере она наверное и так была неплохой, тут стандартная проблема «страны внутри страны»).


                    1. GreyCat
                      01.07.2016 11:44
                      +1

                      С моей точки зрения и из моего опыта — люди, которые ищут себе занятие и возможность получения практики и опыта с 1-2 курса — самые адекватные. Да, это не суперзвезды, но уже сам по себе факт того, что человек осознал, что нужно саморазвиваться — уже очень хороший шаг в нужном направлении.

                      Я занимался сам преподаванием — и в школе, и в институте — и со студентами, и с детьми. У меня дети подобные штуки делали где-то в районе 8-9 класса школы, с поправкой на то, что это, разумеется, был не C (и тем более не C++), и народ не стеснялся добавлять какие-то 3rd party библиотеки, чтобы иметь в своих играх графику. В 11 классе народ уже вовсю писал клиент-серверные сетевые игры.

                      Вот ниже человек отличные ссылки привел — https://habrahabr.ru/post/304448/#comment_9682844 — по-моему, хорошо иллюстрирует то, чем вполне человек может заниматься в 15 лет.


                      1. monah_tuk
                        04.07.2016 07:27

                        Читаю, и создаётся впечатление другой планеты. Или рассы на этой. У нас в школе (90е) информатика была только в 10-11 классе. Выше писали про переводы систем счисления, ЛОГО, qbasic. Лично я знал это к тому времени, мне повезло, попала, лет в десять, книжка в руки — Лекции профессора Фортрана. А в 14 родители компьютер купили (за что им памятник — ведь у военных тогда с деньгами было туго. очень). Т.е. к окончанию школы я, более менее, освоился с бейсиком, паскалем (уровень turbo и delphi 3), Си и крошечку C++ (разные версии Borland C++ до 5.0 и C++Builder 4). А грузануть тогда можно было меня чем угодно: даже вопросом, что такое пузырьковая сортировка. Стек, очередь, прочие структуры данных и так далее — это вообще было за гранью...


                        Маленькая ремарка — интернета не было. Можно было купить модем и тратить по сотне рублей за час в самом дешёвом виде, но родителям бы я это объяснить не смог. Правда это не помешало в районе 9-10 класса обзавестись Linux второй системой :) Линуксоидов в нашем городке не было больше.


                        А у вас в 11 классе уже осилили клиент-серверную архитектуру...


                        1. GreyCat
                          04.07.2016 10:02
                          +1

                          Разница между тем, что я описываю и тем, что вы описываете, в двух вещах: в разнице поколений и в наличии наставник(а|ов), которые подталкивают молодой мозг в нужном направлении.

                          По большому счету, про себя лично я могу рассказать все примерно то же самое, что и вы, с поправкой на то, что компьютер у меня появился раньше, книжек было больше, в школе был доступ к MSX 2 с 6, кажется, класса и т.д. и т.п.

                          Освоить «клиент-серверную архитектуру» люди освоили, понятно, не сами, а им скромно помогали и подталкивали. Рассказали о том, что такое в целом есть, объяснили зачем и почему, показали какие-то примеры и т.д.


                1. Keyten
                  30.06.2016 20:47

                  Я просто оставлю это тут:
                  — Мои 15 лет: https://habrahabr.ru/post/138272/
                  — Мои 18 лет (в день, когда мне исполнилось 18, если точнее, отнять год-два на разработку): https://habrahabr.ru/post/239261/


              1. ZEN_UA
                30.06.2016 22:17
                -5

                Уважаемый, данная статья по моей задумке является выходом за зону типичных консольных задач, и никак не претендует на медаль. Я выложил ее в качестве псевдоразвлекательного материала для подобных мне по уровню изучающих С++. Если Вы дочитали статью, то заметили, что Unix — системы не были целевой платформой.
                В любом случае спасибо за комментарий. Я ценю Ваш отклик и учту при написании следующей статьи.


          1. pavel_pimenov
            30.06.2016 13:43

            memory leaks (new player[] -> но нет delete)

            В коде есть delete [] temp;


    1. drweb63
      30.06.2016 12:44
      -2

      С учётом того, что человек пишет впервые и он первокурсник, то понятно что это похоже на курсовую. Но считаю что в черновики не стоит убирать. Если вы считаете, что для вас задача которую решил автор статьи тривиальна, то для других это может оказаться хорошим подспорьем для начала своего пути, да и просто для того чтобы вспомнить о старых играх и понастольгировать.


      1. Antervis
        30.06.2016 13:11
        -2

        человек, способный разобраться в этом коде, скорее всего будет в состоянии написать подобную игру с нуля.


  1. 1vanK
    30.06.2016 11:54
    +4

    В C++ длинные строки можно разбивать на линии:

    char arr[] = "бла бла бла"
                 "бла бла бла";
    


    1. ZEN_UA
      30.06.2016 22:20

      Спасибо за совет, не знал. :)


  1. SkyHunter
    30.06.2016 12:17
    +1

    Автор молодец. Я вот на первом курсе вместо увлечения подобным пил пиво в парке с друзьями, о чём сейчас бесконечно жалею.

    (Другое дело, что это вряд ли формат Хабра, но по мне так всё равно см. п.1.)


    1. Temirkhan
      30.06.2016 12:30
      +1

      Я за 5 лет университета про C++ только в легендах слышал. Хотя как-то раз нам показывали C#… Трудное детство


      1. Idot
        30.06.2016 14:25
        -4

        Игры вообще-то именно на C++ и пишут, а не на Хаскеле.


        1. Idot
          30.06.2016 20:28

          Поясню: да-да это именно тот случай когда от кода требуется именно скорость (количество кадров в секунду), а не то за что хвалят Хаскель.


          1. inborn_killer
            30.06.2016 20:40

            Поясните ещё раз, пожалуйста, я что-то не понял.


            1. Idot
              01.07.2016 04:07

              Тут возмутились, что игра написана на C++, а не на каком-то новомодном языке. Хотя игры пишут именно на C++ ради скорости. Конечно, в данном случае скорость не требуется, но если он будет писать игры всерьёз то ему их все равно нужно будет писать на C++. Если игры все равно нужно писать на C++, то возмущение тем, что автор писал именно на C++ — неуместно.

              Я про:

              Я за 5 лет университета про C++ только в легендах слышал
              где C++ объявили «устаревшим ненужным динозавром», и намекнули что нужно писать на новомодном языке.


              1. Temirkhan
                01.07.2016 10:37
                +1

                Эмм… Вы часом не в СМИ работаете? Я просто имел ввиду то, что C++ никогда не встречал за тяжелым детством в университете…


                1. Idot
                  01.07.2016 11:11

                  Читая Ваше сообщение можно решить, что Ваша фраза «трудное детство» написана Вами про автора, который писал на C++.


                  1. Temirkhan
                    01.07.2016 12:23

                    Если Вас и вправду интересует мое мнение, я легко отношусь к любым «ошибкам», которые допускает программист, при условии, что он внимает критике и исправляется.


        1. GamePad64
          01.07.2016 02:38
          +2

          Точнее, игровые движки пишут на C++. А логику игры могут уже писать на чём угодно, от JS до Lua.


    1. ScratchBoom
      30.06.2016 12:59
      +6

      И правильно делали. Лучше пиво пить, чем такой код писать.


      1. SkyHunter
        30.06.2016 17:09
        +1

        Можно подумать, ваши первые потуги были примером для подражания. Научится ещё, всё приходит с опытом.


      1. gaki
        30.06.2016 19:01
        +1

        Ну с кодом можно как-то развиваться, расти профессионально, а с пивом как?


      1. ZEN_UA
        30.06.2016 22:24
        -3

        Я понимаю, что программа написана далеко не лучшим образом, и с радостью перепишу ее, когда получу знания о правильном форматировании проекта.


        1. Chaos_Optima
          01.07.2016 11:53
          +3

          Извините, но если вы понимаете, что программа написана плохо и вообще не несёт никакой ценности, то зачем это выкладывать на хабр? Как выше уже правильно заметили для профессионалов ценности нет, для новичков тем более ценности нет, она попросту опасна для них.
          Прочтите Скотта Мейерса и что-то про ооп (только не читайте идеальный код! Пока опыта не наберётесь). Перед тем как выкладывать следующую статью.


          1. Idot
            01.07.2016 12:16

            А если автор желает выслушать здоровую критику?



          1. ZEN_UA
            01.07.2016 13:05

            Данная статья не является обучающим материалом и имеет псевдоразвлекательный характер. Я приношу извинения, что Вы сочли статью неуместной, в следующий раз удостоверюсь, что все соответствует правилам чистого кода.


    1. smartello
      30.06.2016 18:50

      Я тоже пил пиво на горке, но ничего, к экватору устроился на работу, дважды сменил направление деятельности (оставаясь в разработке) и ни о чём не жалею. Чего и вам желаю.


    1. monah_tuk
      04.07.2016 07:41

      Всему своё время. На первом курсе у меня ставка за лабу по информатике была: 10 руб или бутылка пива (было тут местное — Студёное, можно было от 9 до 12 руб за бутылку найти). Пива выпил много. Да курсачи уже денежную таксу брал — к концу первого семестра таки модем смог купить и появиться в интернете (потом было несколько сростов: сначала интернет подешевке, потом и бесплатнее :)). Что бы штамповать курсачи придумалась "гибкая" архитектура — это было первым моим достижением. Она трудилась на меня почти до третьего курса. Потом благодаря пиву и левонетам я нашёл первую работу, причём сразу по суровому: embedded, C, многопоточность, голый Xlib. Всё это немного в перемешку с open source.


      ЗЫ а учился на специальности Электроснабжение, информатика была на допотопных компах с Windows 3.11 и каком-то древнем визуальном бейсике.
      ЗЗЫ начало учебы — 2001 год


  1. nightvich
    30.06.2016 12:19
    +3

    Идея хорошая. Для первого раза вполне годно, реализация для первого курса на хорошем уровне.
    Продолжайте развиваться, вы молодец.


    1. ZEN_UA
      30.06.2016 22:26

      Большое спасибо!


  1. Tujh
    30.06.2016 12:24
    +6

    Поздравляю, опыт безусловно полезный, да и выкладывание на всеобщее обозрение — плюс за смелость.

    Буквально пара замечаний:
    1. Исходные коды лучше всего выкладывать не в саму статью, а в публичный репозиторий, например GitHub, заодно и с технологиями контроля версий, хотя бы поверхностно, познакомишься.
    2. Мешать С и С++ в коде лучше отвыкать сразу.


    1. ZEN_UA
      30.06.2016 22:27

      Спасибо за совет. Про смешивание кода имеется в виду использование FILE*?


      1. win32asm
        01.07.2016 11:14
        +1

        Использование рядом FILE* и std::cout (в смысле, стрима).

        Наверное, ещё вместо 2D массива фиксированного размера (char [14][50]) лучше использовать объект, хранящий std::array, размеры 2d поля и функцию для обращения к элементу по 2м индексам (и передавать его по ссылке, где надо, и из него же читать размеры поля).

        И не бояться использовать const. 8-)


  1. Zheniog
    30.06.2016 12:33
    +2

    Игра не запустилась. Не найдена либа MSVCP140D.dll. Будь я преподом, отправил бы на пересдачу :)


    1. Nipheris
      30.06.2016 12:50
      +12

      Не сочтите за грубость, ничего личного, но из-за таких преподавателей 90% людей на курсе так и не научились пользоваться C++ «в продакшене» — никто так и не рассказал, как и куда совать рантайм, как собрать релизную сборку и вообще как сделать «чтобы работало» хотя бы в одной ОС. Куча людей приходило, запускало exe-шники на компьютере препода, получало такую же ошибку и уходило печалиться со словами «а на Delphi бы написал — все б заработало». В результате преподаватели превращались из наставников в эдаких «потребителей» — объяснить ничего не могут, но надо чтобы всё работало.


      1. Zheniog
        30.06.2016 13:19
        -14

        Это универ, никто и не обязан вам все знания на блюдечке преподносить.


        1. Sirikid
          30.06.2016 13:59
          -15

          Плюсаните кому не жалко за меня


        1. OnYourLips
          30.06.2016 15:19
          +3

          Обязаны. В этом заключается основная задача университетов.

          А «учить учиться», как любят говорить защитники скатившегося образования, обязывает наличие дееспособности, которое у здоровых людей начинается в 18 лет.


          1. Free_ze
            30.06.2016 15:43
            +3

            Ни в одном образовательном стандарте не оговорено каким конкретным языкам программирования и под какую платформу программировать обучать. Есть базовые алгоритмы, есть базовые технологии. Из всего этого складывается теория. Обладая фундаментальными знаниями студент, как будущий инженер, способен **освоить** все необходимое и применить знания на практике.
            А не просто «учить учиться».

            Мне всегда интересно послушать предложения ненавистников «скатившегося образования» на тему «образовательной программы мечты», чтобы не потерялась ни глубина, ни универсальность и уложилось все это хотя бы в специалитет (5 лет).


            1. OnYourLips
              30.06.2016 16:30
              -1

              Какая разница, какой язык используется? Если человек пишет статью, то подразумевается, что он хотя бы знает язык, на котором пишет код. При чем тут образование?

              > Обладая фундаментальными знаниями студент, как будущий инженер, способен
              А если не обладает? Если студенту просто не дали фундаментальные знания в должном объёме?
              Если самое сложное, что проходили в вузе, были массивы и базовые алгоритмы, а на std::vector выделили всего один урок?

              P.S. «Спасибо» за шестнадцатый минус в карму за этот комментарий.


              1. Free_ze
                30.06.2016 17:03
                +3

                Это я к тому, что информация об отличиях дебажной версии от релизной не входит в образовательную программу по естественным причинам.

                > А если не обладает?

                Про структуры данных рассказали? Вот, честно, мне сложно представить, что можно рассказывать про std::vector больше одной пары.
                Нам вовсе запрещали использовать контейнеры и алгоритмы STL (вообще какой-либо внешней библиотеки) для практических работ в курсе обучения основам программирования. Мы были вынуждены писать свои велосипеды, благодаря которым студенты понимали, как это реализовано внутри, по каким принципам, как это сделать оптимально. А уж узнать, что такое уже существует в стандартной библиотеке и как этим пользоваться — дело на полчаса (почитать референс).

                ЗЫ Я на вашу карму влиять не могу, так что заберите свое «спасибо» обратно.


        1. igorch96
          30.06.2016 15:37
          +2

          И кому такой универ нужен? Проще самому книжки почитать и разобраться. Будет и быстрее и дешевле, да и качество знаний значительно выше, чем в таком универе…


          1. Boletus
            30.06.2016 16:05
            -1

            Люди приходят в университет и учатся. Потом начинают работать и узнают массу вещей, о которых думали иначе или не знали вообще. Я еще никогда не слышал о таком, чтобы человек пришел в университет заранее зная «как надо» (ну или хотя бы «как не надо»), и смог бы прийти к выводу как у вас (самому почитать книжки и разобраться), и этому выводу последовал.

            Кроме того, университет университетом (можно самому научиться, можно и переучиться), а диплом — дипломом. Его можно либо получить, либо купить. Чтение книг его не принесет, а наличие диплома интересуют многих. И не всегда вообще у человека есть обширный выбор куда же пойти учиться.


          1. Idot
            30.06.2016 17:09
            +3

            Универ даёт гарантированную стандартную теоретическую базу. А при самостоятельном изучении есть большой соблазн эту базу пропустить.


            1. xenohunter
              30.06.2016 19:29
              +1

              У меня не техническое образование, но, научившись по книгам и статьям, работаю программистом, и соблазн для меня как раз в обратном — изучить эту самую стандартную теоретическую базу.


              1. Free_ze
                30.06.2016 19:38
                +2

                Это не соблазн. Вы ее уже пропустили. А теперь нужда тянет вас ее учить.


                1. xenohunter
                  01.07.2016 11:56

                  Вовсе нет. Frontend-разработчикам редко нужна, например, физика. Или теория чисел. И, несмотря на то, что это сложно, это ещё и дико интересно, и изучать такое — именно соблазн. И, конечно, хочется перестать быть только фронтендом.


                  1. Free_ze
                    01.07.2016 12:05

                    В первом предлоджении вы говорите «вовсе нет», а в крайнем себе противоречите. Ведь если у вас есть соблазн выйти за рамки фронта, то для этого требуются новые знания. Cуть в мотивации: интересно именно заниматься чем-то другим, а не изучать прикладные науки ради изучения.
                    Халтура в учебе же самообоснована.


                    1. xenohunter
                      01.07.2016 13:09
                      +1

                      Хм. Я понимаю вашу точку зрения, но мой посыл — в том, что обучение само по себе интересно. И базовые знания сами по себе если и требуются, то всё равно приятны в изучении.


          1. prishelec
            01.07.2016 03:28

            У нас в универе был курс по созданию RSS лент.
            Препод сразу сказал: уже в течении двух лет когда я слышу фразу «у меня не работает», я отвечаю – и фиг с ним.
            Это потом мы узнали, что он учился в аспирантуре. И ему нужно было просто проводить занятия и принять зачет.
            Основная сила в самообразовании.


            1. Idot
              01.07.2016 04:12

              Потому что когда устроишься на работу нужна будет твоя способность к самообучению (ни у кого нет времени нянчиться с новичком). А если человек привык, что за него всё разжёвывают, то…


      1. Tujh
        30.06.2016 14:00

        Во-первых, xxxD.dll говорит, что код отладочный, сдавать обычно всё же требуется релизную версию («как пережить релиз» очень известная статья). Во-вторых, вопрос, как отвязать программу от «redist» студии, ну или что этот пакет должен быть вместе с исполняемым файлом — вероятно второй по популярности вопрос на форумах разработки на VS. Это преподаватель не обязан объяснять, так как сильно специфичный момент, вы же не будете требовать, что бы он ещё и список библиотек для приложения написанного на Qt+MinGW рассказывал, или будете?
        Я вообще за то, что бы преподаватели учили чистым языкам, а не «студиям»/«билдерам», так как объяснять потом человеку, например, что такое строки в С++ (а особенно в чистом Си) и почему нет «стандартных» IntToStr желания всё меньше и меньше.


        1. pavel_pimenov
          30.06.2016 14:13
          +1

          Почему сдавать обычно требует релизную версию? в ней ведь нет assert-ов и у препода будет меньше шансов ее «уронить».


          1. Tujh
            30.06.2016 14:21

            Как раз релизную версию уронить легче, если она сама не упадёт по каким-то причинам. Отладочная защищает от выхода за границы массива, добавляя до и после по несколько байт «мусора», часто переменные инициируются нулём или «волшебным числом».
            Я ведь не зря упомянул статью Как пережить релиз


        1. lemelisk
          30.06.2016 14:28
          +3

          нет «стандартных» IntToStr
          Начиная с С++11 есть std::to_string()


          1. Tujh
            30.06.2016 14:41
            +1

            Я как бы в курсе. А вот учебные планы — нет.

            Вообще строки для многих проблема, как показывает практика. То, что в Си строка — это тот же массив байт и вот так сделать просто нельзя:

            char str1[] = "string one";
            char str2[] = "string two";
            char str3[] = str1 + str2
            понимают далеко не все.
            А зачем нужен std::string.c_str() и что он делает могут объяснить вообще единицы выпускников.


        1. Nipheris
          01.07.2016 03:27
          +1

          > вероятно второй по популярности вопрос на форумах разработки на VS
          Да, возможно. Но этот вопрос нужно еще правильно задать. Нужно знать, ЧТО спросить.

          > Я вообще за то, что бы преподаватели учили чистым языкам, а не «студиям»/«билдерам»
          А кто говорит, что надо учить Студии или Билдеру? Вы не понимаете сути проблемы. Объснять надо не о том, что есть какая-то там DLL-ка у VC++-шного рантайма. Это достаточно всколзь упомянуть, добавив «ищите больше в MSDN», потратив в сумме на все это ~25 секунд. А вот что действительно нужно объяснить — что C++ как ПЛАТФОРМА (а не как язык) — вещь гибкая и разносторонняя. Что в большинстве тулчейнов есть понятие конфигурации (согласитесь, это ж не обязательно про билдер или студию). Что так или иначе существует библиотека времени выполнения, и нужно как-то с ней линковаться. Что ни malloc, ни new магическим образом не появятся в готовом бинарнике.
          А про внешние библиотеки это вообще отдельная история. И тут тоже я не вижу особой зависимости от конкретного продукта IDE — всякому компилятору нужны хедеры, всякому линковщику нужны объектные файлы и файлы библиотек, и неважно какого они формата. Вы ж понимаете, что дело не в том, что студент не знает, в каком окошке в Студии include path настраивается. Студент не знает, что это в принципе НУЖНО ДЕЛАТЬ. Он не знает, что готовый бинарник нужно слинковать с каким-нибудь Qt Widgets, которым он окошки нарисовал (точнее, пытается нарисовать).
          И тут встаёт вопрос — а что такое учить «чистым языкам»? Выходит, C++ как платформу тоже нужно учить? Написал и запустил — такое может и прокатит в Шарпе или Питоне, но раз преподаётся C++ — значит, нужно и цикл разработки/деплоя затронуть? Представьте что вы изучали бы C# или Java, а вам бы не стали рассказывать, что такое сборка/пакет.

          В сферическом университете в вакууме конечно должна быть именно теоретическая подготовка, и курсов по конкретным ЯП вообще быть не должно. Но тогда должна быть нормальная ПРАКТИКА. Желательно на ПРОИЗВОДСТВЕ. У большинства инженерных специальностей всегда так и было. С комбайнами и станками плотно знакомились именно на заводах. А с IT у нас как сейчас? Практика есть на производстве? Мы вот за все 5 лет стен университета не покидали.

          Так что нужно восполнять пробел. Как я уже выше говорил, из-за чтения C++ как «чистого языка» у нас народ так и программировал до самого диплома в лучшем случае на C# (20% максимум), еще немного PHP/Python, а все остальные так и остались на Делфи, уже тогда (в 2010 году) никому не нужном.
          Только не говорите, что это эдакий «фильтр» для настоящих инженеров. Таких фильтров было и без того навалом, например когда курсы читались вообще не в том порядке, в каком надо было.


          1. Idot
            01.07.2016 04:45

            Если вчерашнего студента взяли на работу, то предполагается что он должен уметь разбираться с подобными мелкими проблемами — САМ! Ни у кого нет времени с ним нянчиться. Так что если он научился разбираться с подобными проблемами САМ — то молодец, а иначе он будет отнимать время у своих коллег.

            PS Блин! Я всем практическим навыкам обучался сам, а ВУЗе мне дали необходимую теоретическую базу и тот же многократно упоминаемый в этой теме Delphi изучил сам (тогда давали ForTran, на который мы перешли с учебно-тренировочного BASIC), да и Pascal я тоже изучил сам, и C++ и так далее. Что за блин поколение лентяев пошло?!


            1. Nipheris
              01.07.2016 12:33

              > Ни у кого нет времени с ним нянчиться.
              А вы не считаете это проблемой? Для меня «нянчится» — это когда на Тостере очередной студент копипаст код всей своей лабы и спрашивает — где у меня ошибка? Вместо того, чтобы научиться наконец дебажить, локализовать проблему и задать конкретный вопрос.

              Я тоже Бейсик изучал еще с 8-го класса, а на C# начал писать еще на 1-м курсе, в то время как читали нам его на 4-м. Вопрос не в этом.

              Вопрос в том, что системный подход в высшем образовании сейчас отсутствует. Мы с вами могли самостоятельно обучаться навыкам потому, что сейчас компьютеры очень доступны. И информация сейчас максимально доступна. Но вы представляете себе инженера машиностроения, который всем практическим навыкам обучается сам? Может ли он дома экспериментировать с роботом-сварщиком?) Не думаю, что у всех есть такая возможность.

              То, что в IT по стечению обстоятельств имеется возможность обучаться самому не значит, что образование должно на это рассчитывать. Понимаете в чем дело, я согласен с вами что лекции в ВУЗе — это место для теории графов и алгоритмов, и даже не совсем правильно читать лекции по конкретным «языкам» — но в противном случае у студента должна быть возможность в конце каждого курса выбрать практические занятия (в универе или даже на производстве), и полноценно в них поучаствовать, чтобы наконец проложить мост между алгоритмом Дийкстры и Git-ом с MinGW и Qt.

              Вообще, высшее образование сегодня выполняет немного не те задачи. Я считаю, очень сильно не хватает колледжей, которых а) хватило бы многим людям для достижения желаемого уровня навыков; б) после которых часть людей могла бы продолжить образование в ВУЗе (и тут бы остались те, кому действительно стоит получить хорошую теоретическую базу). Сейчас из-за культа полуобязательности высшего образования для инженерных специальностей масса людей приходит в ВУЗ «научиться программировать» и завтра же найти работу, а им дают алгоритмы и теорию на всю жизнь.

              Про школу я вообще молчу. В соседних комментариях пишут про написание клиент-серверных игр в 11 классе — для меня это что-то совершенно невообразимое. Наш учитель по информатике не то что программировать, Винду поставить бы не смог сам. Я правда из маленького города, наверное в крупных все иначе.


              1. Idot
                01.07.2016 13:04
                +1

                на Тостере очередной студент копипаст код всей своей лабы и спрашивает — где у меня ошибка?

                В ДНК :)
                (надеюсь помните этот анекдот)


            1. Nipheris
              01.07.2016 12:41

              И да, fregate хорошо сказал — неправильно и так все умеют. Программирование сейчас это все еще искусство. Очень многие вещи не формализованы и не изучены — одно разнообразие ЯП и холивары про них дают понять, что люди еще не понимают, какие инструменты для чего применять. И выходит, что в практическом плане есть очень много ремесленных секретов, вроде тех, что изложены в «Совершенном коде» Макконнела и других подобных книгах. Но такие книги полезно читать, когда ты уже набил шишек и насмотреля проблем. Студенты не будут читать такую литературу. И получается, что в идеале нужно идти на пару лет к «мастеру Джавы», и перенимать его мудрость. Вы скажете — «это называется стажировка». А я скажу, что сегодня только лучшие студенты в достаточно развитых городах могут её получать. Когда в городе нет очень крупных компаний, которые могут себе позволить взять под крыло студента, стажировок ему не видать.


      1. Fqyeh29
        30.06.2016 15:36
        +1

        Увы и у меня в вузе так. (я сам вот только только сессию 1го курса закрыл)

        Есть у нас препод, задал лабы на C# и все. На вопросы «а как делать», «а где у меня ошибка?» ответ «ищи в интернете».

        Вот так как то… И в итоге за 1й курс 3-4 человека освоили c#.


        1. Free_ze
          30.06.2016 15:46
          -1

          И лекций по программированию у вас нет, и литературу тоже не посоветовали, да?


        1. Idot
          01.07.2016 06:29

          Есть у нас препод, задал лабы на C# и все. На вопросы «а как делать», «а где у меня ошибка?» ответ...

          На работе с Вами тоже никто нянчиться не будет. Представьте, устроились Вы на работу и спрашиваете своего начальника «а как делать», «а где у меня ошибка?». Представили?


          1. fregate
            01.07.2016 07:38
            +2

            Как-то странно преположить, что в университете не должны хотя бы показать как правильно. Как неправильно и так все умеют. Если есть ошибки, то их разбирают, если не индивидуально, то после каких-то общих лабораторных работ показывают самые распространенные и как их обходить. Преподаватель проверяет код в любом случае. А иначе… что такое обучение программированию в университете? Рассказать про базовые алгоритмы и оценку сложности их? Что дальше? Все? Программисты готовы? Можно их отправлять в продакшн? )
            И странно сравнение между работой и университетом. Да, я считаю, что в универститете должны помогать разбирать ошибки, показывать как надо и как не надо и тд и тп.
            В общем-то и на работе, взяв джуниора, ему также будут подсказывать и показывать, пока он не освоится с кодобазой и принципаими работы.


          1. Temirkhan
            01.07.2016 13:05
            -1

            Зачем же тогда вообще нужна песочница в виде школы и вуза? Давайте уж сразу всех в бой.


        1. zolkko
          02.07.2016 02:39
          +2

          Какой у вас злой препод. В совсем недавнем прошлом многие программировать начинали в 10-12 лет. Программы писали в тетрадках, а игрушки перепечатывали с книжек с выключенным телевизором, что бы кинескоп и зрение не садилось =]
          А 18 летний дядя слишком нежен стал.
          Отчислить половину группы — вторая заботает C# за вечер. Но в типовом вузе преподу никто не позволит такого сделать, времена не те.


          1. zolkko
            02.07.2016 02:51
            -1

            Прям какой-то Хогвардс с Кашпировскими, ей-богу. Поставь там баночку с мочёй, а через 5 лет, глядишь, а там уже senior .net developr.


      1. billyevans
        02.07.2016 14:20
        +1

        Я приносил обычно образ виртуалки с самой виртуальной машиной, когда могли быть какие то сложности с запуском.


  1. kay
    30.06.2016 12:39
    -2

    Автор — молодец. Небольшое замечание: использование плюсов здесь — излишество.


  1. kx13
    30.06.2016 12:44
    -2

    По поводу бинарника. Не запускается, выдает следующую ошибку:
    The program can't star because MSVCP140D.dll is missing from you computer.


  1. mikhailt
    30.06.2016 12:44
    -3

    Круто! Работает!
    Только вместо русских букв знаки вопроса.
    Система Windows 7 x64 [Version 6.1.7601]


  1. Free_ze
    30.06.2016 12:44
    +16

    Код полон магический констант и месиво из бизнес-логики и графики. И про С++ здесь лишь I/O, в остальном здесь используется сишная стандартная библиотека. Вопрос: причем здесь хаб C++?

    ЗЫ Если честно, это формат какого-нибудь Pikabu.


    1. lyubick
      30.06.2016 15:36

      Согласен с Вами, должен отметить, что если для компиляции программы необходим g++ это не делает её программой на С++. Всё-таки хотелось бы более объектно-ориентированный поход.


    1. Idot
      30.06.2016 17:10

      У человека — первый блин комом. Просто надо ему помочь понять что у него не так и почему.


      1. Free_ze
        30.06.2016 17:37
        +1

        Ну почему комом? Для первого курса это неплохой результат. Но хабр — это все-таки профессиональное сообщество, где ждут статьи о чем-то инновационном или хотя бы best practices. Ценности в целой статье меньше, чем в одной ссылке со сборником таких игрушек, которую ниже приводили.


  1. correy
    30.06.2016 12:44
    -11

    Очень даже неплохо для первого курса, на мой взгляд


  1. Arkafon
    30.06.2016 12:44
    +4

    Собственно, а каких комментариев вы ждёте? Единственный плюс сделанной работы — доведена до конца.


  1. GlukKazan
    30.06.2016 12:56

    Пробило на ностальгию. Где ты тёплая и почти ламповая Д3-28 моей молодости?
    Кто её помнит, поймёт, почему пробило.


    1. estet
      30.06.2016 13:03
      +2

      Тоже обожаю старые текстовые игры, поэтому собрал их в один список. В некоторые можно поиграть на удаленном сервере, для других записал демо в asciinema.


      1. MrMerak
        30.06.2016 13:34

        а это не ваш сайт? http://osgameclones.com/


        1. estet
          30.06.2016 14:53

          Нет, это другой человек делал.


  1. Antervis
    30.06.2016 13:09
    +1

    Пара настоятельных рекоммендаций:
    1. Почитай c++ coding style. Их несколько, выбери по нраву и придерживайся.
    2. Не используй «магические числа». Есть enum, можно задать глобальную константу (начиная с c++11 есть constexpr), на худой конец задефайни. (вытекает из п.1.)


  1. Idot
    30.06.2016 13:11
    -1

    Спасибо! Вспомнил молодость, как подобное писал на BASIC под ДОСРВ/М (Дисковая Операционная Система Реального Времени).


  1. borisko
    30.06.2016 13:16
    +2

    Даже если не придираться к стилю кода и прочим вещам, основная проблема (и я удивлён, что на неё до сих пор никто не указал) — использование рекурсии в том формате, в котором её обычно используют в ФП для передачи состояния.
    Чтобы не тратить стек и не ограничивать сверху возможное время работы программы, нужно было обойтись циклом с явной обработкой текущего состояния экрана/игры или хотя бы goto.


  1. fogone
    30.06.2016 13:22
    +1

    Адское месиво. Автор про абстракции и код-стайл похоже вообще не в курсе.


  1. MrMerak
    30.06.2016 13:23
    -1

    — SpaceInvader.exe — Системная ошибка
    — Запуск программы невозможен, так как на компьютере отсутствует MSVCP140D.dll. Попробуйте переустановить программу.
    — ОК


  1. barker
    30.06.2016 13:36
    +11

    Почему все пишут «неплохо для первого курса»? Если это первый курс профильной специальности (кстати, судя по времени года он уже закончился), то так-то не очень.


    1. Tujh
      30.06.2016 14:04
      +4

      Я слишком давно закончил ВУЗ, но тогда профильные предметы начинались едва ли не с третьего курса. Первые — буквально азы, ни каких алгоритмов, code-style и прочего, только «пишем вот так чтобы вывести строку на экран и не задаём вопросов» (с).
      Обрадуйте, что сейчас всё поменялось? :)


      1. Ddnn
        30.06.2016 15:38

        От многих вещей зависит — от преподавателя, от направления подготовки, от вуза. У нас далеко не самая «кодерская» специальность, но даже нам на первом курсе дали некоторые простые алгоритмы и структуры данных(динамический массив и список сами писали), чуть-чуть ООП, чуть-чуть шаблонов, тонко намекнули про google coding style в конце первого семестра. Тем не менее, преподаватель хоть и упоминал иногда, как сделать хорошо, обычно пользовался логикой «ну работает и ладно».
        На профильных направлениях ( вроде «Программной инженерии») материала в разы больше, и требования намного выше (даже с оформлением комментариев очень строго).


      1. programmer4neo
        30.06.2016 15:38
        +1

        К сожалению, воз и нынче там. Не смотря на смену инструментов подход по-прежнему старый. При чем как в вузах (в этом году имел удовольствие заканчивать смежную специальность и если бы не увлекался программированием со времен Делфи 3 то фиг бы что я понял), что в школах — в свежезаконченом учебном году вел профильный факультатив — сказать что программа отходы жизнедеятельности — ни сказать ничего. Простой пример попробуйте обучать С++ имея методичку по древнему паскалю в лучшем случае или по бейсику в худшем.


      1. Labunsky
        30.06.2016 20:12
        +1

        Все зависит от вуза и специальности. Имею смелость учиться на компьютерной безопасности, все основы, code-style и прочее успели подать за первые два курса, при этом для какой-нибудь джавы даже успели углубиться в тонкости вроде многопоточности. Дальше только лучше, разве что научные руководители для курсовых все так же не проявляют интереса к серьезным темам.

        Взамен, такую активность многие не выдерживают, поэтому очень велик процент списывания и зубрежки вместо учебы. В итоге, к 4му курсу отсеилось больше половины потока, а из оставшихся немногие действительно разбираются в профильных предметах.


  1. inborn_killer
    30.06.2016 13:50
    +3

    Хм… По-моему, на первом курсе я программировал примерно так же. Хорошо, что в то время хабра ещё не было, а интернет был по талонам.


  1. BaDP1nG
    30.06.2016 14:49
    +2

    А я на курсовую выходил с проектом TaxiOnMap(ещё под симбиан, был руководителем проекта). Тоже говорили, мол «зачем мобильные приложения для такси, если такси можно вызвать по телефону»? Сейчас, когда пришёл Убер, этим преподам, надеюсь, очень стыдно.)


  1. alex-khv
    30.06.2016 15:36
    +8

    Я такое в 10 классе писал. Будучи студентом (техникума в далеком мухосранске) в тетради на asm для x86 написал код змейки для граф режима 320x200*256 (удобнее чем 640x480*16 в котором байт содержит несколько пикселей). Потом в той же тетради перевел код в опкоды и на Volcov Commander в шестнадцатеричном редакторе записал опкоды в com файл. После этого дописал адреса ближнего и дальнего прыжка jpm инструкций. Это были 90е годы, только книжки и никаких интернетов.
    А вы говорите автор молодец.


    1. roman12rus
      30.06.2016 17:06

      Вот у меня тоже мысль сразу же возникла про ассемблер для подобных игр.


    1. Idot
      30.06.2016 17:13
      -1

      Ну, он хотя бы хоть что-то попытался сделать сам. А то тут некоторые пишут, что ВУЗ ему должен был положить всё это в рот уже разжёванное.


    1. Idot
      30.06.2016 17:31
      -1

      PS на asm конечно круто — аплодирую Вам стоя!
      (я то в школе подобное на обычном BASIC писал)


      1. roman12rus
        30.06.2016 17:45
        -2

        В школе и я на BASIC для Спектрума писал, asm пытался осилить, но не допёр. Год назад поступил на второе высшее на программиста, так asm8 и asm16 вместе с С пошли отлично. Для детей написал на asm игру типа как у автора, но скроллинг вертикальный. Колбэки в JS для меня пока гораздо сложнее, чем asm. Правда, я JS меньше месяца мучаю.


  1. Boletus
    30.06.2016 15:36
    +1

    Святослав, не слушай злобных комментаторов, пиши дальше. Ну, если и не статьи, то код — точно!

    Замечания и мысли.

    1) Присоединюсь к тем, кто говорит что код — не С++. Его надо либо переписать на С++, либо очистить до С. Оба варианта — хорошие. Если идти по пути С, то выбрасывание С++-ной библиотеки приведет к серьезнейшему похуданию результирующего бинарника.

    2) Некоторые люди не смогли запустить твою игру. У них возникла проблема с redistributable-библиотекой, потому что твоя сборка — не release, а debug, и требует иных библиотек. Почитай про это и попробуй сделать release-build.

    3) Если будешь исследовать консоль Windows и дальше, то обрати внимание на позиционирование курсора. Есть в WinAPI такой вызов: SetConsoleCursorPosition (https://msdn.microsoft.com/en-us/library/windows/desktop/ms686025(v=vs.85).aspx). С помощью него можно устанавливать курсор по координатам (x, y). В статье есть родственные вызовы, позволяющие менять цвет курсора. Можно усложнить игру, сделав разные слои — под игровое пространство, под HUD, под что-то еще.


    1. playermet
      30.06.2016 22:46
      +1

      > Есть в WinAPI такой вызов: SetConsoleCursorPosition
      Имхо, если делать текстовые игры под WinAPI, то лучше рисовать в буферы а затем выводить их в консоль целиком. Намного удобней организовать рисование и скорость на порядки выше.
      Хорошая статья по теме: www.randygaul.net/2011/11/16/windows-console-game-writing-to-the-console


    1. gasizdat
      30.06.2016 23:01
      +1

      Не нужно вредных советов, release тут ни при чем (ну разве, что будет требовать не MSVCP140D.dll, а MSVCP140.dll). Компилировать нужно со статическим c-runtime (/MT).


  1. Honeysusami
    30.06.2016 15:37
    +5

    Я придерживаюсь мнения, что новые статьи — есть хорошо, но они должны быть качественными. Пиши ещё, только учти все замечания.
    Конкретные предложения ( некоторые высказвыались выше ):
    1. Код лучше через систему контроля версий выложить куда-нибудь в open source (github наример).
    2. Также ( лично моё пожелание) весь код прятать в спойлеры.
    3. Смешивание чистого С и С++ надо избегать всеми силами (Кнут насколько я понмню хорошо объясняет разницу).
    4. Разделения логики на уровни\компоненты\части ( вот тут можно было бы использовать ООП возможности С++ например).
    5. К предыдущему пункту — разделение ( логическое ) на файлы.
    6. Найти, выбрать и придерживаться определённого код-стайла.
    7. Все константные числа и строки лучше организованно хранить в перечислениях \ словарях или отельных файлах (в случае локализации или внесении изменений в описании избавит от попа-боли).
    8. Лучше разбивать логику на простейшие операции и выносить в отдельные фукнции и более осмысленно давать имена переменным (рекомендую почитать Р. Мартина «Чистый код»).
    9. К предыдущему пункту использование больших вложенностей и рекурсий для передачи состояния.
    10. Отвязка от использования платформо зависимых типов и библиотек.


  1. dima_mendeleev
    30.06.2016 15:40
    +1

    Было бы круто гифку с демонстрацией игры добавить в пост.


  1. abikvl
    30.06.2016 17:37
    +1

    Автор, ну сделай уже нормальную релизную сборку со статически связанным рантаймом, хочу поиграться! :)


    1. ZEN_UA
      30.06.2016 23:00

      Сделал, жду отзыв :)


      1. abikvl
        01.07.2016 07:05

        Сделал, но на половину. :) По прежнему не запускается на моей машине. Я бы мог сам скачать необходимый рантайм и запустить, но в данном случае, как простой пользователь, просто жалуюсь.

        Не буду комментировать качество кода, потому что, во-первых, тут уже другие постарались, во-вторых, понимание архитектуры, кодинг-стайла, как и фундаментальные знания, появятся с опытом. Но понимать, что такое линковка, чем отличается статическая библиотека от динамической, нужно уже сейчас, раз ты выкладываешь свой продукт на всеобщее обозрение.

        Вот тут примерно то, что нужно сделать:
        http://stackoverflow.com/questions/37398/how-do-i-make-a-fully-statically-linked-exe-with-visual-studio-express-2005


      1. abikvl
        01.07.2016 07:15

        Кстати, идентификаторы на английском без всякого транслита радуют.


  1. gaki
    01.07.2016 05:59
    +1

    Ну я бы на первом курсе, может, и хуже написал :)
    Первым делом, конечно, надо убрать все эти длиннющие строки из текста самой программы в отдельные файлы. Сделать отдельные «графические ресурсы», хоть и текстовые. Придумать структуру файлов для хранения космического корабля, меню, уровней и т. п. Читать эти данные при инициализации программы. Помимо прочего, это позволит менять данные без перекомпиляции и сильно сократит исходный код.
    Во-вторых, вообще сильно сократить исходный код. За пассажи вида case 0: «октябырь», case 1: «ноябырь», по-хорошему, надо бы отрывать руки — что мешает сложить строки в массив и просто сделать cout << months[i]? Кроме того, вообще поискать в коде похожие друг на друга куски и подумать, нет ли в них чего-то общего, что можно было бы оформить в виде отдельной функции. И даже не повторяющийся код можно, для ясности, разбить на функции, согласно тому, что он делает — типа функция отрисовки корабля в позиции х, у, функция обновления спидометра и т. п.
    В-третьих, чётко разделить логическое состояние игры и его отображение на экране. По-поводу того, как и зачем это сделать, можно просто открыть в википедии статью «Model-view-controller» и вдумчиво прочитать.
    В-четвёртых, для общего развития попробовать портировать всё это под Линукс. Не потому, что линуксоиды тоже очень захотят поиграть, а потому, что в процессе узнаешь для себя много нового :) Если задор ещё не иссякнет, можно даже попробовать сделать так, чтобы одни и те же исходники собирались и под Линукс, и под Виндоус.


  1. MixailPetrenko
    01.07.2016 11:14

    А мне статья понравилась — интересно почитать, что делали другие. Я в 7-м классе изучал C++ и написал «сапёр» для командной строки.


  1. dm_urievich
    01.07.2016 11:15
    -1

    Просмотрел код, понастольгировал.
    Ведь я тоже с такого кода начинал: все в числах, полно непонятных if — else, через месяц проще заново написать чем что-либо править.

    Молодец, больше практики, почитай книги, прислушайся к советам в комментах и с тебя вырастет хороший программист.


  1. Heavy_RU
    02.07.2016 00:56
    -2

    Подскажи пожалуйста литературу по которой учишься