Guest User

Untitled

a guest
Feb 23rd, 2023
78
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 11.82 KB | None | 0 0
  1. /*
  2.  * (c) Cranium, aka Череп. 2014
  3.  * GNU GPL
  4.  *
  5.  */
  6.  
  7. #include "CGame.h"
  8.  
  9. #include <iostream>
  10. #include <cstring>
  11. #include <conio.h>
  12.  
  13. // форматная строка для форматирования результата игры
  14. const char *recordFormatStr = "%-15s  %9.4f  %4u  %7.2f  %s";
  15.  
  16. SRecord::SRecord() {
  17.     name[0] = '\0';
  18.     rating = 0.0;
  19.     length = 0;
  20.     game_time = 0;
  21.     date = static_cast<time_t>(0);
  22. }
  23.  
  24. void SRecord::as_string(char *buffer) {
  25.     sprintf(buffer, recordFormatStr, name, rating, length, game_time, ctime(&date));
  26. }
  27.  
  28. ostream& operator << (ostream& os, const SRecord& rec) {
  29.     os
  30.         << rec.rating << ' '
  31.         << rec.length << ' '
  32.         << rec.game_time << ' '
  33.         << rec.date << ' '
  34.         << rec.name << endl;
  35.     return os;
  36. }
  37.  
  38. istream& operator >> (istream& is, SRecord& rec) {
  39.     is
  40.         >> rec.rating
  41.         >> rec.length
  42.         >> rec.game_time
  43.         >> rec.date;
  44.     is.ignore(1);
  45.     is.getline(&rec.name[0], 16);
  46.     return is;
  47. }
  48.  
  49. // Функция сравнения результатов по рейтингу.
  50. // Необходима для работы qsort() для сортировки по убыванию.
  51. int rec_compare(const void *_op1, const void *_op2) {
  52.     const SRecord *op1 = reinterpret_cast<const SRecord *>(_op1);
  53.     const SRecord *op2 = reinterpret_cast<const SRecord *>(_op2);
  54.     return static_cast<int>(op2->rating - op1->rating);
  55. }
  56.  
  57. /*----- end of struct SRecord -------------------------------------*/
  58.  
  59.  
  60. // очистка буфера клавиатуры
  61. void clearkeys() {
  62.     while (_kbhit())
  63.         _getch();
  64. }
  65.  
  66. // Конструктор
  67. // _scr     - объект, осуществляющий вывод на консоль
  68. // _width   - ширина игрового поля (x)
  69. // _height  - высота игрового поля (y)
  70. // _latency - задержка между изменением позиции в миллисекундах
  71.  
  72. CGame::CGame(CScreen& _scr, int _width, int _height, int _latency) :
  73.     width(_width), height(_height), latency(_latency), scr(_scr) {
  74.  
  75.     srand(static_cast<unsigned int>(time(NULL)));
  76.  
  77.     duration_game = 0;
  78.     rating = 0.0;
  79.  
  80.     // инициализация таблицы команд управления игрой
  81.     cmd_table[0] = CmdPair(27, CMD_EXIT);   // escape key
  82.     cmd_table[1] = CmdPair('K', CMD_LEFT);  // стрелка влево
  83.     cmd_table[2] = CmdPair('M', CMD_RIGHT); // стрелка вправо
  84.     cmd_table[3] = CmdPair('H', CMD_UP);    // стрелка вверх
  85.     cmd_table[4] = CmdPair('P', CMD_DOWN);  // стрелка вниз
  86. }
  87.  
  88. CGame::Command CGame::get_command() {
  89.     int ch;
  90.  
  91.     ch = _getch();
  92.     if (ch == 0 || ch == 0xe0) {
  93.         ch = _getch();
  94.     }
  95.  
  96.     for (int i = 0; i < 5; i++) {
  97.         if (cmd_table[i].first == ch) {
  98.             return cmd_table[i].second;
  99.         }
  100.     }
  101.     return CMD_NOCOMMAND;
  102. }
  103.  
  104. // Координата еды вычисляется случайным образом.
  105. // Ограничение: координата не должна попадать в тело змеи.
  106. SCoord CGame::make_food() {
  107.     SCoord food;
  108.     do {
  109.         food.x = rand() % (width - 2) + 1;
  110.         food.y = rand() % (height - 2) + 1;
  111.     } while (snake.into(food));
  112.  
  113.     return food;
  114. }
  115.  
  116.  
  117. const char BORDER = '#';    // символ для рисования рамки игрового поля
  118.  
  119.  
  120. void CGame::draw_field() {
  121.  
  122.     scr.cls();
  123.  
  124.     for (int y = 0; y < height; y++) {
  125.         if (y == 0 || y == height - 1) {
  126.             for (int x = 0; x < width; x++)
  127.                 scr.pos(x, y, BORDER);
  128.         }
  129.         else {
  130.             scr.pos(0, y, BORDER);
  131.             scr.pos(width - 1, y, BORDER);
  132.         }
  133.     }
  134.     scr.pos(0, height);
  135.     _cprintf("Length: ****  Rating: ****.**** (****.****)  Time: ****.**");
  136. }
  137.  
  138.  
  139. void CGame::print_stat() {
  140.     scr.pos(8, height);
  141.     _cprintf("%04u", snake.size());
  142.     scr.pos(22, height);
  143.     _cprintf("%09.4f", rating);
  144.     scr.pos(33, height);
  145.     _cprintf("%09.4f", rating_i);
  146.     scr.pos(51, height);
  147.     _cprintf("%07.2f", duration_game);
  148. }
  149.  
  150. void CGame::top10_table() {
  151.     scr.cls();
  152.     char buf[80];
  153.  
  154.     scr.pos_str(width / 2 - 12, 2, "***** T O P    1 0 *****");
  155.     scr.pos_str(5, 4, "Name              Rating    Length  Time   Date");
  156.  
  157.  
  158.     for (int i = 0; i < 10; i++) {
  159.         ttop10[i].as_string(buf);
  160.         scr.pos_str(5, 5 + i, buf);
  161.     }
  162. }
  163.  
  164. void CGame::top10(bool after_game) {
  165.  
  166.     char buf[80];
  167.     char buf_encoded[NAMELENGTH];
  168.  
  169.     top10_table();      // показать таблицу 10 лучших результатов
  170.  
  171.     time_t date = time(NULL);
  172.     if (after_game) {
  173.         // если игра была сыграна, то показать текущий результат
  174.         scr.pos(5, 16);
  175.         _cprintf(recordFormatStr, "Your result", rating, snake.size(), duration_game, ctime(&date));
  176.     }
  177.  
  178.     if (rating > ttop10[9].rating) {    // если рейтинг игры больше, чем меньший из 10 лучших...
  179.         // запросить имя игрока
  180.         scr.pos_str(5, 20, "Your name: _");
  181.         scr.pos(16, 20);
  182.         cin.getline(&buf[0], NAMELENGTH);
  183.         clearkeys();
  184.         OemToCharBuff(buf, buf_encoded, static_cast<DWORD>(NAMELENGTH));
  185.         // заменить последнюю запись в таблице 10 лучших результатов
  186.         strcpy(ttop10[9].name, buf_encoded);
  187.         ttop10[9].date = date;
  188.         ttop10[9].game_time = duration_game;
  189.         ttop10[9].length = snake.size();
  190.         ttop10[9].rating = rating;
  191.         // отсортировать результаты по убыванию
  192.         qsort(ttop10, 10, sizeof(SRecord), rec_compare);
  193.         // обновить таблицу на экране
  194.         top10_table();
  195.  
  196.         // обновить файл с 10 лучшими результатами
  197.         write_top10();
  198.     }
  199. }
  200.  
  201. void CGame::pak(int y) {
  202.     scr.pos_str(width / 2 - 15, y, "Press any key for continue...");
  203.     _getch();
  204.     clearkeys();
  205. }
  206.  
  207. bool CGame::once_more() {
  208.     scr.pos_str(width / 2 - 12, height - 3, "O n c e    m o r e ?");
  209.  
  210.     int ch = _getch();
  211.     clearkeys();
  212.     if (ch == 'N' || ch == 'n' || ch == 27)
  213.         return false;
  214.     return true;
  215. }
  216.  
  217. const char *top10_filename = "snake.dat";   // имя файла для хранения 10 лучших результатов
  218.  
  219. void CGame::read_top10() {
  220.     ifstream fin(top10_filename);
  221.     if (fin) {
  222.         for (int i = 0; i < 10; i++)
  223.             fin >> ttop10[i];
  224.     }
  225.     fin.close();
  226. }
  227.  
  228. void CGame::write_top10() {
  229.     ofstream fout(top10_filename);
  230.     if (fout) {
  231.         for (int i = 0; i < 10; i++)
  232.             fout << ttop10[i];
  233.     }
  234.     fout.close();
  235. }
  236.  
  237. const char *ver_number = "v 1.1";
  238. const char *copyright = "(c) Cranium, 2014.";
  239.  
  240. void CGame::logo() {
  241.     scr.pos_str(width / 2 - 9, 10, "O l d s c h o o l");
  242.     scr.pos_str(width / 2 - 7, 12, "S  N  A  K  E");
  243.     scr.pos_str(width / 2 - 3, 16, ver_number);
  244.     scr.pos_str(width / 2 - 9, height, copyright);
  245.     pak(22);
  246. }
  247.  
  248. void CGame::goodbye() {
  249.     scr.cls();
  250.     _cprintf("Oldschool Snake %s\n%s\n", ver_number, copyright);
  251. }
  252.  
  253.  
  254. const char FOOD = '$';      // символ для вывода еды
  255.  
  256. void CGame::game_loop() {
  257.  
  258.     duration_game = 0;
  259.     rating = rating_i = 0.0;
  260.  
  261.     draw_field();           // нарисовать игровое поле
  262.  
  263.     snake.reset(SCoord(width / 2, height / 2));     // установить змею: длина 2,
  264.                                                     // положение - в середине игрового поля,
  265.                                                     // направление - влево
  266.     Command cmd = CMD_NOCOMMAND;
  267.     State stt = STATE_OK;
  268.     // delta  содержит приращение координат (dx, dy) для каждого перемещения змеи по полю
  269.     SCoord delta(-1, 0);                // начальное движение - влево
  270.     SCoord food = make_food();          // вычислить координаты еды
  271.     scr.pos(food.x, food.y, FOOD);      // вывести еду на экран
  272.  
  273.     snake.draw(scr);                    // первичное рисование змеи
  274.  
  275.     print_stat();                       // вывести начальную статистику игры
  276.  
  277.     clock_t time1, time2, duration;
  278.     time1 = clock();
  279.  
  280.     do {
  281.  
  282.         if (_kbhit())                   // если в буфере клавиатуры есть информация,
  283.             cmd = get_command();        // то принять команду
  284.  
  285.         // обработка команд
  286.         switch (cmd) {
  287.         case CMD_LEFT:
  288.             delta = SCoord(-1, 0);
  289.             break;
  290.         case CMD_RIGHT:
  291.             delta = SCoord(1, 0);
  292.             break;
  293.         case CMD_UP:
  294.             delta = SCoord(0, -1);
  295.             break;
  296.         case CMD_DOWN:
  297.             delta = SCoord(0, 1);
  298.             break;
  299.         case CMD_EXIT:
  300.             stt = STATE_EXIT;
  301.         default:
  302.             break;
  303.         };
  304.  
  305.         SCoord hd = snake.head();       // координата головы змеи
  306.         hd += delta;                    // координата головы змеи после приращения (следующая позиция)
  307.         // если голова змеи столкнулась с границей поля или со телом змеи, то змея умирает
  308.         if (hd.x == 0 || hd.x == width-1 || hd.y == 0 || hd.y == height-1 || snake.into(hd))
  309.             stt = STATE_DIED;
  310.  
  311.         if (stt == STATE_OK) {          // если змея ещё жива, то
  312.             snake.move(delta, scr);     // сдвинуть змею на delta
  313.  
  314.             if (snake.head() == food) { // если координата головы змеи совпадает с координатой еды, то
  315.                 snake.grow(food, 3);    // увеличить длину змеи
  316.                 food = make_food();     // вычислить координаты новой еды
  317.                 scr.pos(food.x, food.y, FOOD); // вывести еду на экран
  318.  
  319.                 // Вычисление времени игры, частичного и общего рейтинга.
  320.                 // Частичный рейтинг вычисляется как длина змеи, делённая на время в секундах,
  321.                 // затраченное на подход к еде (время от съедения предыдущей еды до съедения следующей).
  322.                 // Таким образом, чем чаще змея ест и чем она длиннее, тем выше частичный рейтинг.
  323.                 time2 = clock();
  324.                 duration = time2 - time1;
  325.                 duration_game += static_cast<double>(duration) / CLOCKS_PER_SEC;
  326.                 rating_i = static_cast<double>(snake.size()) / duration * CLOCKS_PER_SEC;
  327.                 rating += rating_i;     // общий рейтинг - сумма частичных рейтингов за игру
  328.                 time1 = time2;
  329.  
  330.                 print_stat();           // вывод текущей статистики игры
  331.             }
  332.  
  333.             Sleep(latency);             // задержка перед следующим изменением позиции
  334.         }
  335.  
  336.     } while (stt == STATE_OK);          // играем, пока змея жива
  337.  
  338.     scr.pos_str(width / 2 - 8, 10, " G a m e    o v e r ");
  339.     clearkeys();
  340.     _getch();
  341.     clearkeys();
  342. }
Add Comment
Please, Sign In to add comment