Опыт работы с нейронными сетями и простой код на Ассемблере.

Тема в разделе "WASM.BEGINNERS", создана пользователем dcc0, 17 май 2026.

Метки:
  1. dcc0

    dcc0 Active Member

    Публикаций:
    2
    Регистрация:
    22 дек 2022
    Сообщения:
    136
    who_know777,
    Оно хорошо, но строк уже за 500.
    А я хотел выпилить всё так, чтобы идея рейкастинга и эффект 3D сохранились.
    Так, чтобы ухватить алгоритм и по шагам и в целом. Много графики и доп. эффекты отвлекают. Педагогический (для себя) смысл теряется .
     
  2. Application

    Application Active Member

    Публикаций:
    1
    Регистрация:
    29 мар 2021
    Сообщения:
    380
    Графическая версия показывает некоторые баги консольной:
    - неправильно считаются столкновения, игрок проваливается внутрь стены
    - игрок цепляется за стены

    Мне кажется у игрока должна быть форма чтобы правильно считать коллизии в 3d.
     
    dcc0 нравится это.
  3. dcc0

    dcc0 Active Member

    Публикаций:
    2
    Регистрация:
    22 дек 2022
    Сообщения:
    136
    who_know777, да за пределы стены можно выйти лучом.
    Тут есть проверка на это:
    Код (C):
    1.  if (testX < 0 || testX >= MAP_WIDTH || testY < 0 || testY >= MAP_HEIGHT) {
    --- Сообщение объединено, 6 июл 2026 ---
    Вот здесь, конечно, интересная математика. И тут бы хорошие комментарии:
    Зависимость, как я понял, такая:
    Угол игрока влияет на угол луча (что естественно).
    Код (Text):
    1.          
    2.   /*Пример: Угол луча равен. (0- (3.14159265358979323846/3)/2) + (x (двойной точности, на певрой итерации 0) / 80)*(3.14159265358979323846/3)*/
    3.             rayAngle = (playerAngle - FOV / 2.0) + ((double)x / (double)SCREEN_WIDTH) * FOV;    

    Угол луча влияет на взгляд (игрока) по x и y
    Код (Text):
    1.  eyeX = cos(rayAngle);
    2.             eyeY = sin(rayAngle);
    3.  
    Взгляд по x и y влияет на луч по x и y, понятно, что луч от позиции игрока.
    Код (Text):
    1.            
    2.  distanceToWall += 0.1;  /* шаг по лучу*/
    3.                  testX = (int)(playerX + eyeX * distanceToWall); /*переменная луча по X*/
    4.                  testY = (int)(playerY + eyeY * distanceToWall); /*по Y*/
    5.  
    А для вывода столбца оказывается важным только, где остановился
    distanceToWall (эффект рыбьего глаза не трогаю). На дистанцию до стены влиеят все оставльное по цепочке.
    Потолок = высота экрана / 2 - высотка экрана / дистанция до стены
    Пол = высотка экрана - потолок.

    Далее вывод: если y < потолка печатем пробел
    Если y > потолка и y <= пола, печатаем символы - стены с тенями.
    В остальном печатем пол - пробел.


    P.S.
    Вроде и просто. Алгоритм стал понятен.
    И даже почему так много отдельных переменных.
    Но угол луча (математически) не сказать, что очень очевидная вещь.
     
    Последнее редактирование: 6 июл 2026
    Application нравится это.
  4. Application

    Application Active Member

    Публикаций:
    1
    Регистрация:
    29 мар 2021
    Сообщения:
    380
    Мне недавно дипсик выдал что есть проблема в рейкастинге связаная с коллизиями.
    И что она решается так то и так то. Потерял его ответ блин.

    Немного тупанул, надо было линии везде рисовать таким образом:
    Код (Pascal):
    1.  
    2. procedure DrawLine(hDC: HDC; X1, Y1, X2, Y2: Integer;
    3.                    Color: COLORREF; Thickness: Integer = 1;
    4.                    Style: TLineStyle = lsSolid);
    5. var
    6.   Pen: HPEN;
    7.   OldPen: HPEN;
    8.   PenStyle: DWORD;
    9. begin
    10.   // Инициализируем PenStyle значением по умолчанию
    11.   PenStyle := PS_SOLID;
    12.  
    13.   case Style of
    14.     lsSolid:   PenStyle := PS_SOLID;
    15.     lsDashed:  PenStyle := PS_DASH;
    16.     lsDotted:  PenStyle := PS_DOT;
    17.     lsDashDot: PenStyle := PS_DASHDOT;
    18.   end;
    19.  
    20.   Pen := CreatePen(PenStyle, Thickness, Color);
    21.   OldPen := SelectObject(hDC, Pen);
    22.  
    23.   MoveToEx(hDC, X1, Y1, nil);
    24.   LineTo(hDC, X2, Y2);
    25.  
    26.   SelectObject(hDC, OldPen);
    27.   DeleteObject(Pen);
    28. end;
    p.s. этот рейкастинг для меня открыл новый дивный мир в погромировании.
    Кроме него еще есть bsp деревья. В общем интересно это все, и не на один год.
     
    dcc0 нравится это.
  5. dcc0

    dcc0 Active Member

    Публикаций:
    2
    Регистрация:
    22 дек 2022
    Сообщения:
    136
    Я так понимаю, что для сокращения вот эти переменные можно закомментировать и перенести:
    Код (Text):
    1.  
    2. eyeX = cos(rayAngle);
    3. eyeY = sin(rayAngle);
    4.  
    Т.е. в этом фрагменте можно сократить так:

    Код (C):
    1. for (y = 0; y < SCREEN_HEIGHT; y++) {
    2.         for (x = 0; x < SCREEN_WIDTH; x++) {
    3.               /*Вычисляем угол для каждого луча*/
    4.              /*Угол луча равен. (0- (3.14159265358979323846/3)/2) + (x (двойной точности) / 80)*(3.14159265358979323846/3)*/
    5.             rayAngle = (playerAngle - FOV / 2.0) + ((double)x / (double)SCREEN_WIDTH) * FOV;
    6.             distanceToWall = 0.0;
    7.              hit = 0;
    8.             while (!hit && distanceToWall < MAX_DEPTH) {
    9.                 distanceToWall += 0.1;  /* шаг по лучу*/
    10.                  testX = (int)(playerX + cos(rayAngle) * distanceToWall); /*переменная луча по X*/
    11.                  testY = (int)(playerY + sin(rayAngle) * distanceToWall); /*по Y*/
    12.  
    13.                 if (testX < 0 || testX >= MAP_WIDTH || testY < 0 || testY >= MAP_HEIGHT) {
    14.                     hit = 1;  /*луч вышел за границы карты, считаем, что столкнулся с границей*/
    15.                     distanceToWall = MAX_DEPTH;
    16.                 } else {
    17.                     if (worldMap[testY][testX] == 1) {
    18.                         hit = 1; /*столкновение со стеной*/
    19.                     }
    20.                 }
    21.             }
    --- Сообщение объединено, 6 июл 2026 ---


    Кто хоть раз мерил углы и размеры помещения, или улицы (топографы), или пещеры (спелеологи) даже... тем, наверное, алгоритм более понятен. =)
    Я имею в виду, откуда берутся синусы с косинусами.

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

    Даже с ходу понятно, почему x до SCREEN_WIDTH. Так как лучом обходим всю видимую область, а этом и есть экран.
    Но в играх часто - это может быть и не весь экран.

    Мне кажется, но я не уверен, что игрок проваливается за стены по причине, что на каждой итерации угол луча считается с
    учетом x - а x тут - это всего лишь переменная для цикла.

    Он вообще переводит int x в (double)x. Можно сказать даже, что в общем, - это число с потолка взято. Определяет изменение угла луча для каждого броска.
    --- Сообщение объединено, 6 июл 2026 ---
    Т.е. если совсем в двух словах алгоритм такой:
    1) Посчитал угол луча от позиции игрока.
    2) Бросил луч.
    3) Получил дистанцию.
    Повторил все шаги для всей видимой области - ширины экрана.
    Вывод:
    1) Посчитал потолок и пол на основе дистнации для каждого стобца стены.
    2) Вывел каждый столбец стены.
    --- Сообщение объединено, 6 июл 2026 ---
    В стиле матрицы:

    Код (C):
    1. /*Модель 3D Raycasting на C(89). Написана нейронной сетью. Отредактирована by dcc0.
    2. Стены с помощью символов
    3. Компиляция и запуск gcc -o ray ray.c  -lm; ./ray
    4. * или gcc -std=c89  -o ray ray.c  -lm; ./ray
    5. Убран эффект рыбьего глаза*/
    6. #include <stdio.h>
    7. #include <math.h>
    8. #include <string.h>
    9. #include <stdlib.h>
    10. #include <time.h>
    11. /*Размеры карты и экрана*/
    12. #define MAP_WIDTH 16
    13. #define MAP_HEIGHT 16
    14. #define SCREEN_WIDTH 80
    15. #define SCREEN_HEIGHT 24
    16. #define M_PI 3.14159265358979323846
    17. #define FOV (M_PI / 3.0)
    18. #define MAX_DEPTH 16.0
    19.  
    20. /*Карта*/
    21. int worldMap[MAP_HEIGHT][MAP_WIDTH] = {
    22.   {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
    23.   {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
    24.   {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
    25.   {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
    26.   {1,0,0,0,1,1,1,0,0,1,1,1,0,0,0,1},
    27.   {1,0,0,0,1,0,1,0,0,1,0,1,0,0,0,1},
    28.   {1,0,0,0,1,1,1,0,0,1,1,1,0,0,0,1},
    29.   {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
    30.   {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
    31.   {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
    32.   {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
    33.   {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
    34.   {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
    35.   {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
    36.   {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
    37.   {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}
    38. };
    39.  
    40. double playerX = 3.0;  /*Начальные координаты игрока*/
    41. double playerY = 3.0;
    42. double playerAngle = 0.0;
    43.  
    44. void draw() {
    45.    int y, x, RayX, RayY, hit, ceiling, floor, rrandd;
    46.    double angleDifference;
    47.    double eyeX, eyeY, rayAngle, distanceToWall;
    48.   printf("\033[2J"); //Очистка экрана
    49.   printf("\033[H"); //И сброс курсора в начало
    50.   fflush(stdout);
    51.  
    52.   for (y = 0; y < SCREEN_HEIGHT; y++) {
    53.   for (x = 0; x < SCREEN_WIDTH; x++) {
    54.         /*Вычисляем угол для каждого луча*/
    55.   rayAngle = (playerAngle - FOV / 2.0) + ((double)x / (double)SCREEN_WIDTH) * FOV;
    56.   distanceToWall = 0.0;
    57.   hit = 0;
    58.   /*Можно удалить */
    59.   //eyeX = cos(rayAngle);
    60.   //eyeY = sin(rayAngle);
    61.   while (!hit && distanceToWall < MAX_DEPTH) {
    62.   distanceToWall += 0.1;  /* шаг по лучу*/
    63.   // testX = (int)(playerX + eyeX * distanceToWall); /*переменная луча по X*/
    64.   //testY = (int)(playerY + eyeY * distanceToWall); /*по Y*/
    65.   RayX = (int)(playerX + cos(rayAngle) * distanceToWall); /*переменная луча по X*/
    66.   RayY = (int)(playerY + sin(rayAngle) * distanceToWall); /*по Y*/
    67.  
    68.   if (RayX < 0 || RayX >= MAP_WIDTH || RayY < 0 || RayY >= MAP_HEIGHT) {
    69.   hit = 1;  /*луч вышел за границы карты, считаем, что столкнулся с границей*/
    70.   distanceToWall = MAX_DEPTH;
    71.   } else {
    72.   if (worldMap[RayY][RayX] == 1) {
    73.   hit = 1; /*столкновение со стеной*/
    74.   }
    75.   }
    76.   }
    77.  
    78.        /*Корректировка расстояния для устранения эффекта рыбьего глаза*/
    79.         angleDifference = rayAngle - playerAngle;
    80.        if (angleDifference > M_PI) angleDifference -= 2 * M_PI;
    81.        if (angleDifference < -M_PI) angleDifference += 2 * M_PI;
    82.  
    83.   ceiling = (int)((SCREEN_HEIGHT / 2.0) - SCREEN_HEIGHT / distanceToWall);
    84.   floor = SCREEN_HEIGHT - ceiling;
    85.  
    86.   srand(time(NULL));
    87.         rrandd = (rand() %10);
    88.  
    89.   if (y < ceiling) {
    90.   printf(" "); /*рисуем пробел - небо*/
    91.   } else if (y > ceiling && y <= floor) {
    92.   if (distanceToWall <= MAX_DEPTH / 4.0) {
    93.            printf("\033[0;32m");
    94.   printf("%d",  rrandd ); /*рисуем стены разной жирности*/
    95.   } else if (distanceToWall < MAX_DEPTH / 2.0) {
    96.   printf("+");
    97.   } else {
    98.   printf(".");
    99.   }
    100.   } else {
    101.          printf("\033[0;32m");
    102.   printf(" "); /*рисуем пол*/
    103.   }
    104.   }
    105.   printf("\n");
    106.   }
    107. }
    108.  
    109. int main() {
    110.   char command[10];
    111.   double newX,  newY;
    112.   char c;
    113. //Управление
    114.   while (1) {
    115.      //На каждое движение перерисовываем видимую область
    116.   draw();
    117.   printf("Введите  (W/A/S/D + enter, 'exit'  - выход): ");
    118.   if (!fgets(command, sizeof(command), stdin)) break;
    119.  
    120.   if (strncmp(command, "exit", 4) == 0) {
    121.   break; /*Выход из программы*/
    122.   }
    123.  
    124.   c = command[0];
    125.   if (c == 'w' || c == 'W') {
    126.   /*Вперед*/
    127.   newX = playerX + cos(playerAngle) * 0.2;
    128.   newY = playerY + sin(playerAngle) * 0.2;
    129.   if (worldMap[(int)newY][(int)newX] == 0) {
    130.   playerX = newX;
    131.   playerY = newY;
    132.   }
    133.   } else if (c == 's' || c == 'S') {
    134.   /*Назад*/
    135.   newX = playerX - cos(playerAngle) * 0.2;
    136.   newY = playerY - sin(playerAngle) * 0.2;
    137.   if (worldMap[(int)newY][(int)newX] == 0) {
    138.   playerX = newX;
    139.   playerY = newY;
    140.   }
    141.   } else if (c == 'a' || c == 'A') {
    142.   /*Поворот налево*/
    143.   playerAngle -= 0.1;
    144.   } else if (c == 'd' || c == 'D') {
    145.   /*Поворот направо*/
    146.   playerAngle += 0.1;
    147.   }
    148.   }
    149.   return 0;
    150. }
    151.  
     
    Последнее редактирование: 6 июл 2026
    Application нравится это.
  6. Application

    Application Active Member

    Публикаций:
    1
    Регистрация:
    29 мар 2021
    Сообщения:
    380
    Мне иногда кажется целесообразно вообще сначала в 2d перейти. Ато хаос немного получается =)
    2d(вид сверху) типа фундамента для будущего понимания(качества будущих решений).

    Есть еще bsp-деревья, там как раз про графы вроде.
     
    Последнее редактирование модератором: 6 июл 2026
    Mikl___ и dcc0 нравится это.
  7. dcc0

    dcc0 Active Member

    Публикаций:
    2
    Регистрация:
    22 дек 2022
    Сообщения:
    136
    Если вдуматься, то в общем все, как и в жизни. Т.е. дискретная модель описывает то, как мы это и видим:
    Луч выходит из позиции игрока (playerX и Y), под углом, который вычисляется для каждого заново (rayAngle),
    движется постепенно до встречи со стеной - инкременет (distanceToWall).
    Код (C):
    1.     RayX = (int)(playerX + cos(rayAngle) * distanceToWall); /*переменная луча по X*/
    2.                 RayY = (int)(playerY + sin(rayAngle) * distanceToWall); /*по Y*/
    3.  
    Один из самых, не сказать, что простых моментов - определение угла следующего луча:
    Код (C):
    1. rayAngle = (playerAngle - FOV / 2.0) + ((double)x / (double)SCREEN_WIDTH) * FOV;
    Тут объясние от нейронной сети вполне внятное:



    На вычислении потолка ещё тоже можно споткнуться (почему высота экрана делённая именно на 2?!):
    Код (C):
    1. ceiling = (int)((SCREEN_HEIGHT / 2.0) - SCREEN_HEIGHT / distanceToWall);
    Нейронная сеть (deepai) объяснила и это:

    --- Сообщение объединено, 7 июл 2026 ---
    who_know777, в bsp деревьях ничего не понимаю. Но вот переборная комбинаторика - интересная штука (если без рекурсии).
    Задачи типа найти алгоритм для любых n (хотя бы условно).
    Заодно поиск нестандартных приёмов.
    --- Сообщение объединено, 7 июл 2026 ---
    Вроде понятно стало: угол - минус половина обзора ( так как игрок смотрит вперед, а лучи от него слева и справа).
    + x столбец (из соотношения к шире) * FOV (которое есть Пи/ на три, потому что поле зрения - треть от 180, т.е. вычисление угла )
    Код (C):
    1. rayAngle = (playerAngle - FOV / 2.0) + ((double)x / (double)SCREEN_WIDTH) * FOV;
    2.  
    --- Сообщение объединено, 7 июл 2026 ---
    Кстати, вспомнил.
    Рейкастинг я пробовал писать даже на php:
    http://i92625up.beget.tech/raycastingModelPhp/main.php
    =)
    --- Сообщение объединено, 7 июл 2026 ---
    2д уже пробовал делать на php:
    http://i92625up.beget.tech/raycastingModelPhp/main.php
     
    Последнее редактирование: 7 июл 2026
  8. Application

    Application Active Member

    Публикаций:
    1
    Регистрация:
    29 мар 2021
    Сообщения:
    380
    Интересный эксперимент.

    Я думаю надо сначала на 2d движок написать:
    - Генерация карты.
    - Перемещение по карте.
    - Обработка коллизий с обьектами.
    - Интеллект врагов(враг всегда знает где игрок, но если игрок далеко враг блуждает по карте).
    - Угол поворота врагов относительно игрока для правильной установки 2d спрайтов(понадобится в будущем).
    - Полет пули игрока/врагов по карте(отдельная структура).
    - и т. д.
     
    dcc0 нравится это.
  9. dcc0

    dcc0 Active Member

    Публикаций:
    2
    Регистрация:
    22 дек 2022
    Сообщения:
    136

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

    http://i92625up.beget.tech/chat/ind/index.php
    --- Сообщение объединено, 7 июл 2026 ---
    who_know777, имхо, если писать, то для терминала продолжить тему. Можно без аски, но лучше с ним... Так как для терминала графика должна быть максимально примитивная.
    Генерация уровней. Враги. Пушка. Счёт. В идеале, если это будет онлайн игра. Кто-то создаёт сервер, кто коннектится. Или сервер на сайте, карта подгружается из бд и к ней все игроки коннектятся... Регистрируются на карте... И играют.... Реализацию в общем виду, но таких игр много...
    --- Сообщение объединено, 7 июл 2026 ---
    Прошу прощения за опечатки, иногда в транспорте набираю.
     
    Последнее редактирование: 7 июл 2026
    Mikl___ и Application нравится это.
  10. dcc0

    dcc0 Active Member

    Публикаций:
    2
    Регистрация:
    22 дек 2022
    Сообщения:
    136
    Попросил конверртировать в php (а думали, что 3d на php невозможно).
    И сделать кнопки через input.

    Код (Text):
    1. <?php
    2. session_start();
    3.  
    4. // Размеры карты и параметры
    5. define('MAP_WIDTH', 16);
    6. define('MAP_HEIGHT', 16);
    7. define('SCREEN_WIDTH', 60);
    8. define('SCREEN_HEIGHT', 24);
    9. define('FOV', M_PI / 3.0);
    10. define('MAX_DEPTH', 16.0);
    11.  
    12. // Карта
    13. $worldMap = [
    14.     [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
    15.     [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
    16.     [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
    17.     [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
    18.     [1,0,0,0,1,1,1,0,0,1,1,1,0,0,0,1],
    19.     [1,0,0,0,1,0,1,0,0,1,0,1,0,0,0,1],
    20.     [1,0,0,0,1,1,1,0,0,1,1,1,0,0,0,1],
    21.     [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
    22.     [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
    23.     [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
    24.     [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
    25.     [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
    26.     [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
    27.     [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
    28.     [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
    29.     [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]
    30. ];
    31.  
    32. // Инициализация или получение состояния игрока
    33. if (!isset($_SESSION['playerX'])) {
    34.     $_SESSION['playerX'] = 3.0;
    35.     $_SESSION['playerY'] = 3.0;
    36.     $_SESSION['playerAngle'] = 0.0;
    37. }
    38.  
    39. // Обработка команд
    40. if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    41.     $command = $_POST['command'] ?? '';
    42.  
    43.     $playerX = &$_SESSION['playerX'];
    44.     $playerY = &$_SESSION['playerY'];
    45.     $playerAngle = &$_SESSION['playerAngle'];
    46.  
    47.     $newX = $playerX;
    48.     $newY = $playerY;
    49.  
    50.     switch ($command) {
    51.         case 'W':
    52.         case 'w':
    53.             $newX = $playerX + cos($playerAngle) * 0.2;
    54.             $newY = $playerY + sin($playerAngle) * 0.2;
    55.             if ($worldMap[(int)$newY][(int)$newX] === 0) {
    56.                 $playerX = $newX;
    57.                 $playerY = $newY;
    58.             }
    59.             break;
    60.         case 'S':
    61.         case 's':
    62.             $newX = $playerX - cos($playerAngle) * 0.2;
    63.             $newY = $playerY - sin($playerAngle) * 0.2;
    64.             if ($worldMap[(int)$newY][(int)$newX] === 0) {
    65.                 $playerX = $newX;
    66.                 $playerY = $newY;
    67.             }
    68.             break;
    69.         case 'A':
    70.         case 'a':
    71.             $playerAngle -= 0.1;
    72.             break;
    73.         case 'D':
    74.         case 'd':
    75.             $playerAngle += 0.1;
    76.             break;
    77.     }
    78. }
    79.  
    80. // Функция рисования
    81. function draw() {
    82.     global $worldMap, $_SESSION;
    83.  
    84.     $playerX = $_SESSION['playerX'];
    85.     $playerY = $_SESSION['playerY'];
    86.     $playerAngle = $_SESSION['playerAngle'];
    87.  
    88.  
    89.     for ($y = 0; $y < SCREEN_HEIGHT; $y++) {
    90.         for ($x = 0; $x < SCREEN_WIDTH; $x++) {
    91.             $rayAngle = ($playerAngle - FOV / 2) + (($x / SCREEN_WIDTH) * FOV);
    92.             $distanceToWall = 0.0;
    93.             $hit = false;
    94.  
    95.             while (!$hit && $distanceToWall < MAX_DEPTH) {
    96.                 $distanceToWall += 0.1;
    97.                 $rayX = (int)($playerX + cos($rayAngle) * $distanceToWall);
    98.                 $rayY = (int)($playerY + sin($rayAngle) * $distanceToWall);
    99.  
    100.                 if ($rayX < 0 || $rayX >= MAP_WIDTH || $rayY < 0 || $rayY >= MAP_HEIGHT) {
    101.                     $hit = true;
    102.                     $distanceToWall = MAX_DEPTH;
    103.                 } else {
    104.                     if ($worldMap[$rayY][$rayX] == 1) {
    105.                         $hit = true;
    106.                     }
    107.                 }
    108.             }
    109.  
    110.             $angleDiff = $rayAngle - $playerAngle;
    111.             if ($angleDiff > M_PI) $angleDiff -= 2 * M_PI;
    112.             if ($angleDiff < -M_PI) $angleDiff += 2 * M_PI;
    113.  
    114.             $ceiling = (int)((SCREEN_HEIGHT / 2.0) - SCREEN_HEIGHT / $distanceToWall);
    115.             $floor = SCREEN_HEIGHT - $ceiling;
    116.  
    117.             if ($y < $ceiling) {
    118.                 $output .= " ";
    119.             } elseif ($y > $ceiling && $y <= $floor) {
    120.                 if ($distanceToWall <= MAX_DEPTH / 4.0) {
    121.                     $output .= "#";
    122.                 } elseif ($distanceToWall < MAX_DEPTH / 2.0) {
    123.                     $output .= "+";
    124.                 } else {
    125.                     $output .= ".";
    126.                 }
    127.             } else {
    128.                 $output .= " ";
    129.             }
    130.         }
    131.         $output .= "\n";
    132.     }
    133.     return $output;
    134. }
    135.  
    136. ?>
    137.  
    138. <!DOCTYPE html>
    139. <html lang="ru">
    140. <head>
    141. <meta charset="UTF-8" />
    142. <title>3D Raycasting</title>
    143. </head>
    144. <body style="font-family: monospace; white-space: pre;">
    145. <h2>3D Raycasting на PHP</h2>
    146. <pre>
    147. <?php echo draw(); ?>
    148. </pre>
    149. <form method="post">
    150.     <button name="command" value="W">Вперед (W)</button>
    151.     <button name="command" value="S">Назад (S)</button>
    152.     <button name="command" value="A">Поворот налево (A)</button>
    153.     <button name="command" value="D">Поворот направо (D)</button>
    154.     <button type="submit" name="command" value="exit">Выход</button>
    155. </form>
    156. </body>
    157. </html>
    158.  
    --- Сообщение объединено, 9 июл 2026 ---
    Пощупать:
    http://comb.i92625up.beget.tech/raycastingModelPhp/3draycast_by_neural_net.php

    Скрин:

    https://ibb.co/qLk3CMY3
    --- Сообщение объединено, 10 июл 2026 ---
    Типа игры:
    http://comb.i92625up.beget.tech/raycastingModelPhp/3draycast_game_by_neural_net.php
     
  11. dcc0

    dcc0 Active Member

    Публикаций:
    2
    Регистрация:
    22 дек 2022
    Сообщения:
    136
    Еще немного вручную подпилил код C89.
    1) Добавил в C89 матрицу вращения вручную, чтобы угол игрока не увеличивался бесконечно (инкремент/декремент).
    И поворот теперь сразу на 11 градусов.

    2) Упростил формулу вычисления угла луча и прокомментировал.
    3) Убрал проверку выхода за пределы стены, она там не нужна при такой карте.
    https://gitflic.ru/project/dcc0/mix-c-89-php/edit?file=3dray_edited_with_matrix.c

    P.S.
    Т.е. вычисление угла луча теперь максимально понятно.
    Угол луча равен углу игрока минус 0.5 радиана + x * 0.0174532925 (радиана).
    Почему минус 0.5?! Ответ: 0.5 радиана - примерно 30 градусов, что
    есть половина от угла обзора, который где-то 60 градусов.
    Лучи бросаем от самой левой точки на экране. Если не вычитать половину FOV,
    то будет работать, но от точки взгляда игрока и вправо. Т.е. будет рисовать стены только справа от взгляда, 30 градусов,
    а надо все поле обзора - 60 градусов. Поэтому делим на 2, только все это в радианах.

    А Field Of View (поле обзора) и равно примерно 180/Пи. 57 градусов, почти 60.

    Т.е. в итоге вычитаем (FOV) - Pi/3/2 в радианах. Получаем 0.523 и т.д. Округляем или не округляем.
    Почему x * на 0.017453292? Если умножим на 180/pi то получим примерно 1 градус.
    А нам и нужно на каждой итерации бросить луч на 1 градус.

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

    Про отрисовку.
    Остается только на основе дистанции посчитать, где закончится потолок и где начнется пол по вертикали.
    Между этими значениями будет стена.
     
  12. Application

    Application Active Member

    Публикаций:
    1
    Регистрация:
    29 мар 2021
    Сообщения:
    380
  13. dcc0

    dcc0 Active Member

    Публикаций:
    2
    Регистрация:
    22 дек 2022
    Сообщения:
    136
    Код (C):
    1.  
    2. /*Модель 3D Raycasting на C(89). Написана нейронной сетью. Отредактирована by dcc0.
    3. Стены с помощью символов
    4. Компиляция и запуск gcc -o ray ray.c  -lm; ./ray
    5. * Поворот исправлен на матрицу. Формула поиска угла луча упрощена.
    6. * или gcc -std=c89  -o ray ray.c  -lm; ./ray
    7. Убран эффект рыбьего глаза
    8. 1) Добавил в C89 матрицу вращения вручную, чтобы угол игрока не увеличивался бесконечно (инкремент/декремент).
    9. 2) Упростил формулу вычисления угла луча и прокомментировал.
    10. 3) Убрал проверку выхода за пределы стены, она там не нужна при такой карте.*/
    11. #include <stdio.h>
    12. #include <math.h>
    13. #include <string.h>
    14. #include <stdlib.h>
    15. #include <time.h>
    16.  
    17. /*Размеры карты и экрана*/
    18. #define MAP_WIDTH 16
    19. #define MAP_HEIGHT 16
    20. #define SCREEN_WIDTH 60
    21. #define SCREEN_HEIGHT 24
    22. #define M_PI 3.14159265358979323846
    23. #define FOV (M_PI / 3.0)
    24. #define MAX_DEPTH 16.0
    25.  
    26. /*Карта*/
    27. int worldMap[MAP_HEIGHT][MAP_WIDTH] = {
    28.   {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
    29.   {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
    30.   {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
    31.   {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
    32.   {1,0,0,0,1,1,1,0,0,1,1,1,0,0,0,1},
    33.   {1,0,0,0,1,0,1,0,0,1,0,1,0,0,0,1},
    34.   {1,0,0,0,1,1,1,0,0,1,1,1,0,0,0,1},
    35.   {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
    36.   {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
    37.   {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
    38.   {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
    39.   {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
    40.   {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
    41.   {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
    42.   {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
    43.   {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}
    44. };
    45.  
    46. /*Матрица для поворота наблюдателя вокруг себя */
    47. double matrix_angle[33] = {-8, -7.5, -7, -6.5, -6, -5.5, -5, -4.5, -4, -3.5, -3, -2.5, -2,
    48. -1.5, -1, -0.5, 0.0, 0.5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 4.5, 5, 5.5, 6, 6.5, 7, 7.5, 8};
    49.  
    50. double playerX = 3.0;  /*Начальные координаты игрока*/
    51. double playerY = 3.0;
    52. /*double playerAngle = 0.0;*/
    53.  
    54. void draw(double playerAngle) {
    55.   int y, x, RayX, RayY, hit, ceiling, floor, rrandd;
    56.   double angleDifference;
    57.   double eyeX, eyeY, rayAngle, distanceToWall;
    58.   printf("\033[2J"); /*Очистка экрана*/
    59.   printf("\033[H"); /*И сброс курсора в начало*/
    60.   fflush(stdout);
    61.  
    62.  
    63.   for (y = 0; y < SCREEN_HEIGHT; y++) {
    64.   for (x = 0; x < SCREEN_WIDTH; x++) {
    65.   /*Вычисляем угол для каждого луча*/
    66.   /*rayAngle = (playerAngle - FOV / 2.0) + ((double)x / (double)SCREEN_WIDTH) * FOV;*/
    67.   /*Угол игрока минус FOV/2 (30 градусов влево от точки взгляда игрока)*/
    68.   /*  + x умн. на 1 градус в радианах = 0.0174532925 */
    69.   /*Из FOV (= 60 градусов) вычитаем 30 градусов в радианах для левой части от POV (Point of view - точка обзора)*/
    70.   /*Если не вычитать 0.52359878, то получим только половину обзора*/
    71.   /*Но если убрать, все равно будет работать, будто закрыли один глаз*/
    72.   /*Нам надо найти 60 углов для луча, но не от точки взгдяла, а от точки взгляд - 30градусов (в радианах)*/
    73.   /*а это и есть минус 52359878*/
    74.   /*Так тоже можно, но тогда углы будут от точки взгляда, левая часть из обзора пропадет*/
    75.   /*rayAngle = (playerAngle) + ((double)x * 0.0174532925);*/
    76.   rayAngle = (playerAngle - 0.52359878) + ((double)x * 0.0174532925);
    77.   distanceToWall = 0.0;
    78.   hit = 0;
    79.   /*Можно удалить */
    80.   /*eyeX = cos(rayAngle)*/;
    81.   /*eyeY = sin(rayAngle)*/;
    82.   /*Тут луч начианет двигаться*/
    83.   while (!hit && distanceToWall < MAX_DEPTH) {
    84.   distanceToWall += 0.1;  /* шаг по лучу*/
    85.   /* testX = (int)(playerX + eyeX * distanceToWall);*/ /*переменная луча по X*/
    86.   /* testY = (int)(playerY + eyeY * distanceToWall); */ /*по Y*/
    87.   /*Округлим до целого. Это и есть луч. */
    88.   RayX = (int)(playerX + cos(rayAngle) * distanceToWall); /*переменная луча по X*/
    89.   RayY = (int)(playerY + sin(rayAngle) * distanceToWall); /*по Y*/
    90.   /*Можно закомментировать. При такой карте не сработает.
    91.   if (RayX < 0 || RayX >= MAP_WIDTH || RayY < 0 || RayY >= MAP_HEIGHT) {
    92.   hit = 1;  луч вышел за границы карты, считаем, что столкнулся с границей
    93.   distanceToWall = MAX_DEPTH;
    94.   } else {*/
    95.   /*Луч всегда упрется в стену*/
    96.   if (worldMap[RayY][RayX] == 1) {
    97.   hit = 1; /*столкновение со стеной*/
    98.   }
    99.   /*}*/
    100.   }
    101.  
    102.   /* new: Корректировка расстояния для устранения эффекта рыбьего глаза (new) */
    103.   angleDifference = rayAngle - playerAngle; /* расчет разницы углов (new) */
    104.   /*if (angleDifference > M_PI) { angleDifference -= 2 * M_PI; }*/ /* приведение к диапазону (-π, π) (new) */
    105.   /*if (angleDifference < -M_PI) {angleDifference += 2 * M_PI;}*/ /* new */
    106.   distanceToWall = distanceToWall * cos(angleDifference); /* исправление расстояния (new) */
    107.  
    108.   /*Делим на двое высоту находим точку до которой рисовать потолок пробелом */
    109.   ceiling = (int)((SCREEN_HEIGHT / 2.0) - SCREEN_HEIGHT / distanceToWall);
    110.   /*откуда начнется пол, тоже пробелами*/
    111.   floor = SCREEN_HEIGHT - ceiling;
    112.  
    113.   if (y < ceiling) {
    114.   printf(" "); /*рисуем пробел - небо*/
    115.   } else if (y > ceiling && y <= floor) {
    116.   if (distanceToWall <= MAX_DEPTH / 4.0) {
    117.   printf("#"); /*рисуем стены разной жирности*/
    118.   } else if (distanceToWall < MAX_DEPTH / 2.0) {
    119.   printf("+");
    120.   } else {
    121.   printf(".");
    122.   }
    123.   } else {
    124.  
    125.   printf(" "); /*рисуем пол*/
    126.  
    127.   }
    128.  
    129.   }
    130.  
    131.   printf("\n");
    132.  
    133.   }
    134.   printf("\nУгол игрока в радианах: %g\n", playerAngle);
    135.   printf("\nУгол игрока в градусах: %g\n", playerAngle*180/M_PI);
    136.   printf("\nX: %g\n", playerX );
    137.   printf("\nY: %g\n", playerY );  
    138. }
    139.  
    140. int main() {
    141.   char command[10];
    142.   double newX,  newY;
    143.   char c;
    144.   int ma = 16;
    145.   /*Начальный угол игрока = 0.0*/
    146.   double playerAngle = matrix_angle[16];
    147.   /*Управление*/
    148.   while (1) {
    149.   /*На каждое движение перерисовываем видимую область*/
    150.   draw(playerAngle);
    151.   printf("Введите  (W/A/S/D + enter, 'exit'  - выход): ");
    152.   if (!fgets(command, sizeof(command), stdin)) break;
    153.  
    154.   if (strncmp(command, "exit", 4) == 0) {
    155.   break; /*Выход из программы*/
    156.   }
    157.  
    158.   c = command[0];
    159.   if (c == 'w' || c == 'W') {
    160.   /*Вперед*/
    161.   newX = playerX + cos(playerAngle) * 0.2;
    162.   newY = playerY + sin(playerAngle) * 0.2;
    163.   if (worldMap[(int)newY][(int)newX] == 0) {
    164.   playerX = newX;
    165.   playerY = newY;
    166.   }
    167.   } else if (c == 's' || c == 'S') {
    168.   /*Назад*/
    169.   newX = playerX - cos(playerAngle) * 0.2;
    170.   newY = playerY - sin(playerAngle) * 0.2;
    171.   if (worldMap[(int)newY][(int)newX] == 0) {
    172.   playerX = newX;
    173.   playerY = newY;
    174.   }
    175.   } else if (c == 'a' || c == 'A') {
    176.   /*Поворот налево*/
    177.   ma--;
    178.   if (ma < 0) ma = 32;
    179.   if (ma > 32) ma = 0;
    180.   playerAngle = matrix_angle[ma];
    181.   /*playerAngle -= 0.1;*/
    182.   } else if (c == 'd' || c == 'D') {
    183.   /*Поворот направо*/
    184.   ma++;
    185.   if (ma < 0) ma = 32;
    186.   if (ma > 32) ma = 0;
    187.   playerAngle = matrix_angle[ma];
    188.   /*playerAngle += 0.1;*/
    189.   }
    190.   }
    191.  
    192.   return 0;
    193. }
    194.  
    195.  

    Закомментировал проверку диапазона -pi pi.
    В этом коде вывалится из этого диапазона вроде не должна разница углов.
    --- Сообщение объединено, 12 июл 2026 ---
    Человек с cyberforum
    Переложил этот кода на JS:
    https://ivan-enzhaev.github.io/canvas2d/doom-movement-js/
    Но оставил рыбий глаз.

    Без него:
    http://i92625up.beget.tech/js/raycastjs.html
     
    Последнее редактирование: 12 июл 2026
  14. Application

    Application Active Member

    Публикаций:
    1
    Регистрация:
    29 мар 2021
    Сообщения:
    380
    dcc0, будет ли переход на следующий уровень отрисовки? Имхо недостатки/баги после этого будут видны.

    Код (C):
    1.   if (y < ceiling) {
    2.   printf(" "); /*рисуем пробел - небо*/
    3.   } else if (y > ceiling && y <= floor) {
    4.   if (distanceToWall <= MAX_DEPTH / 4.0) {
    5.   printf("#"); /*рисуем стены разной жирности*/
    6.   } else if (distanceToWall < MAX_DEPTH / 2.0) {
    7.   printf("+");
    8.   } else {
    9.   printf(".");
    10.   }
    11.   } else {
    12.   printf(" "); /*рисуем пол*/
    13.   }
    В свое время делал похожий проект на c, всего 3 функции для отрисовки всей игры:
    Код (C):
    1. void DrawRect(HDC hdc, int x, int y, int w, int h, COLORREF color, BOOL filled) {
    2.     HBRUSH br = filled ? CreateSolidBrush(color) : GetStockObject(NULL_BRUSH);
    3.     HPEN pen = CreatePen(PS_SOLID, 1, color);
    4.  
    5.     SelectObject(hdc, pen);
    6.     SelectObject(hdc, br);
    7.     Rectangle(hdc, x, y, x + w, y + h);
    8.  
    9.     DeleteObject(pen);
    10.     DeleteObject(br);
    11. }
    12.  
    13. void DrawLine(HDC hdc, int x1, int y1, int x2, int y2, COLORREF color, int width) {
    14.     HPEN pen = CreatePen(PS_SOLID, width, color);
    15.     HPEN oldPen = SelectObject(hdc, pen);
    16.  
    17.     MoveToEx(hdc, x1, y1, NULL);
    18.     LineTo(hdc, x2, y2);
    19.  
    20.     SelectObject(hdc, oldPen);
    21.     DeleteObject(pen);
    22. }
    23.  
    24. void DrawCircle(HDC hdc, int x, int y, int radius, COLORREF color, BOOL filled) {
    25.     HBRUSH br = filled ? CreateSolidBrush(color) : GetStockObject(NULL_BRUSH);
    26.     HPEN pen = CreatePen(PS_SOLID, 1, color);
    27.  
    28.     SelectObject(hdc, pen);
    29.     SelectObject(hdc, br);
    30.     Ellipse(hdc, x - radius, y - radius, x + radius, y + radius);
    31.  
    32.     DeleteObject(pen);
    33.     DeleteObject(br);
    34. }
     
  15. dcc0

    dcc0 Active Member

    Публикаций:
    2
    Регистрация:
    22 дек 2022
    Сообщения:
    136
    Application, что под этим подразумевается?! Уровень отрисовки!

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

    Что есть эта сама идея?!

    Проекция линейной перспективы на дискретной системе, на плоскости...

    Это не создание и усложнение кода - это декомпозиция и упрощение.
    Для чего?! Чтобы убрать лишнее. Чтобы было максимально очевидно.
     
  16. dcc0

    dcc0 Active Member

    Публикаций:
    2
    Регистрация:
    22 дек 2022
    Сообщения:
    136
    https://habr.com/ru/articles/343016/
    --- Сообщение объединено, 13 июл 2026 ---
    В завершение этой темы оставлю словесное описание метода (как я это вижу). Надеюсь, ничего не упустил:

    Краткое описание метода бросания лучей (Raycasting 3d или псевдо3д).

    Есть карта:
    Высота: 24
    Ширина 60:
    Есть наблюдатель - игрок, у которого есть:
    координаты на карте:
    x = 3,
    y =3.
    Поле зрения игрока: 60 градусов (равное ширине карты в данном случае оно же FOV - field of view. FOV = Pi/3).

    Направление взгляда = 0 (POV - point of view).

    1) Точка зрения всегда на половине от поля зрения FOV.
    Т.е. слева от центральной точки всегда (POV) - 30 градусов и справа от точки зрения +30 градусов.
    Иными словами поле зрения человека примерно 60 градусов или приблизительно 1 рад = 57 градусов.


    (Алгоритм. Три вложенных цикла. Запускается 1-ый цикл от 0 до высоты экрана - 24)

    2) От точки игрока исходят лучи в количестве 60. От самой левой точки до самой правой ( 2-ой цикл внутри первого, от нуля до 60).
    Луч равен 1 градусу.
    Угол луча = угол игрока - fov/2 +(i/60)*fov
    Или угол луча= угол игрока - 0.5 + 1 градус в радианах (0.17...).
    1 градус в радианах = 1*pi/180=0.174... или близкое 1/57.
    Дистанция до стены равна 0.

    2) Луч движется от наблюдателя (3-ий цикл внутри второго)
    к стене под углом 1 градус (к дистанции прибавляется 0.1 на каждой итерации).
    ЛучХ = угол игрока + кос(угл луча) * на инкремент дистанции
    ЛучУ = угол игрока + син(угла луча) * на инкремент дистанции

    3) Столкнулись со стеной. Описано условием. Если на карте ЛучХ и ЛучУ равны 1.
    Остановка. Инкремент дистанции перестал накапливаться.
    Дистанция до стены равна какому-то значению. Допустим, = 4.
    Вышли из 3-его цикла

    4) Исправление рыбьего глаза
    Корректировка дистанции = угол луча - угол игрока
    Дистанция равна = дистанция * кос(корректировка дистанции) = 3.4.

    5) Вычисление потолка и пола, чтобы нарисовать стену.
    Потолок равен (округление до целого) 20/2 - 20/ дистанция = 4.2 (округляем до 4)
    Пол = 20 - потолок = 16

    6 ) Вывод. Рисование стены. (в конце 2-го цикла)
    От 0 до потолка () выводим символ пробел
    От потолка до пола стену - символ.
    От пола до (низа) экрана выводим пробел.

    7) Получаем равномерную линейную перспективу.
    Алгоритм ищет, где печатать каждый символ для всей матрицы экрана -
    24*60 =1440.

    Добавление (управление):
    Нажали на кнопку стрелки или w/a/s/d
    Экран обновился.
    Вся перспектива перерисовалась заново с учетом новых координат игрока или угла.
     
    _edge и Application нравится это.
  17. _edge

    _edge Well-Known Member

    Публикаций:
    1
    Регистрация:
    29 окт 2004
    Сообщения:
    690
    Адрес:
    Russia
    Тема называлась "простой код на Асс.."

    /s

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

    По теме, которую Вы уже хотите завершить: про рейскаст много при желании можно найти публикаций, https://iquilezles.org/articles/ ( https://github.com/calinou/awesome-gamedev ).

    Так или иначе, спасибо за оживление форума )

    p.s. зацените, какую ляпоту нашел https://www.neperos.com/user/eZine / https://www.neperos.com/journal/AbeDemoschool
     
    Последнее редактирование: 13 июл 2026
    dcc0 нравится это.
  18. GRAFik

    GRAFik Active Member

    Публикаций:
    0
    Регистрация:
    14 мар 2020
    Сообщения:
    525
    Как сейчас помню, была тема от, по-моему, DX что-ли, ну или что-то в его стиле и тред назывался: "Как отапливать частный дом от 1,5-вольтовой батарейки". :) А, может это было и на старом КЛ, но DX - он и в Африке DX, суть от этого, по-моему, не меняется. :)
     
  19. dcc0

    dcc0 Active Member

    Публикаций:
    2
    Регистрация:
    22 дек 2022
    Сообщения:
    136
    _edge, Хотел было завершить, но мне стукнуло написать костёр в терминале. Когда-то на JS такое нашел, очень понравился.
    Моя реализация, фактически не думая и не заботясь ни о чем, лишь бы работало (с кучей лишнего).
    Небольшой, так сказать костерок:

    Код (C):
    1. /*Огонь в терминале Linux*/
    2. #include <stdlib.h>
    3. #include <time.h>
    4. #include <stdio.h>
    5. #include <unistd.h>
    6. int main(void) {
    7.     srand((unsigned int)time(NULL));
    8.     int r = rand();              /* число от 0 до RAND_MAX */
    9.     int flame = rand() % 15;     /* число от 0 до 9 */
    10.     int line = 15;   // строка (с 1)
    11.     int col  = 25;  // столбец (с 1)
    12.  
    13.     // Перемещаем курсор и выводим символ
    14.     while(1) {
    15.     printf("\033[2J");   /* очистить весь экран */
    16.     printf("\033[H");    /* переместить курсор в левый верхний угол (1;1) */
    17.     fflush(stdout);
    18.     flame = rand() % 10;
    19.     line = 15;
    20.     while (line > flame) {
    21.     printf("\033[93m\033[%d;%df", line, col);
    22.     putchar('^');
    23.     fflush(stdout);
    24.     usleep(10000);
    25.     line--;
    26.     }
    27.  
    28.     flame = rand() % 15;
    29.     line = 15;
    30.     while (line > flame) {
    31.     printf("\033[91m\033[%d;%df", line, col+1);
    32.     putchar('^');
    33.     fflush(stdout);
    34.     usleep(20000);
    35.     line--;
    36.     }
    37.  
    38.         flame = rand() % 15;
    39.     line = 15;
    40.     while (line > flame+3) {
    41.     printf("\033[91m\033[%d;%df", line, col+2);
    42.     putchar('^');
    43.     fflush(stdout);
    44.     usleep(20000);
    45.     line--;
    46.     }
    47.  
    48.     flame = rand() % 15;
    49.     line = 15;
    50.     while (line > flame) {
    51.     printf("\033[91m\033[%d;%df", line, col+3);
    52.     putchar('^');
    53.     fflush(stdout);
    54.     usleep(20000);
    55.     line--;
    56.     }
    57.  
    58.     flame = rand() % 10;
    59.     line = 15;
    60.     while (line > flame+3) {
    61.     printf("\033[93m\033[%d;%df", line, col+4);
    62.     putchar('^');
    63.     fflush(stdout);
    64.     usleep(10000);
    65.     line--;
    66.     }
    67.  
    68.  
    69.  
    70. }  
    71.     return 0;
    72. }
    73.  
    Довольно качественный костёр от Алисы, но с 4 раза сделала так, как нужно:
    Я бы даже сказал, что красиво получилось.

    Код (C):
    1. /*Костёр в терминале. Алгоритм написан нейронной сетью*/
    2. #include <stdlib.h>
    3. #include <time.h>
    4. #include <stdio.h>
    5. #include <unistd.h>
    6.  
    7. #define ROWS 16
    8. #define COLS 21
    9. #define DELAY_US 40000   /* ~25 FPS */
    10.  
    11. /* Символы для языков пламени */
    12. static const char flame_chars[] = "^*.:";
    13.  
    14. /* Цвет по позиции внутри языка: только красный и жёлтый */
    15. static int color_code(int y_from_bottom, int max_height) {
    16.     /* y_from_bottom: 0 — самый низ языка, max_height-1 — его вершина */
    17.     double ratio = (double)y_from_bottom / (double)(max_height ? max_height : 1);
    18.  
    19.     if (ratio < 0.5) return 91;  /* красный — нижняя половина */
    20.     return 93;                   /* жёлтый — верхняя половина */
    21. }
    22.  
    23. void draw_fire(int heights[COLS]) {
    24.     int x, y;
    25.  
    26.     /* Очистка экрана и возврат курсора в (1,1) */
    27.     printf("\033[2J\033[H");
    28.  
    29.     for (y = 0; y < ROWS; ++y) {
    30.         for (x = 0; x < COLS; ++x) {
    31.             int h = heights[x];
    32.  
    33.             /* Если столбца нет или он ниже текущей строки — пусто или угли */
    34.             if (h <= 0) {
    35.                 if (y == ROWS - 1) {
    36.                     putchar('#');  /* угли в самом низу */
    37.                 } else {
    38.                     putchar(' ');
    39.                 }
    40.                 continue;
    41.             }
    42.  
    43.             /* Самая нижняя строка экрана — это y == ROWS-1.
    44.                Язык растёт вверх, значит, его основание всегда на ROWS-1,
    45.                а верхушка — на ROWS - h. */
    46.             int bottom_row = ROWS - 1;
    47.             int top_row    = ROWS - h;
    48.  
    49.             if (y < top_row || y > bottom_row) {
    50.                 putchar(' ');
    51.                 continue;
    52.             }
    53.  
    54.             /* Позиция внутри языка: 0 — самый низ, h-1 — самая вершина */
    55.             int y_from_bottom = bottom_row - y;
    56.  
    57.             int color = color_code(y_from_bottom, h);
    58.             char ch = flame_chars[rand() % (sizeof(flame_chars) - 1)];
    59.  
    60.             printf("\033[%dm%c", color, ch);
    61.         }
    62.         putchar('\n');
    63.     }
    64.  
    65.     /* Сброс цвета */
    66.     printf("\033[0m");
    67.     fflush(stdout);
    68. }
    69.  
    70. /* Плавное обновление высот */
    71. void update_heights(int heights[COLS], int min_h, int max_h) {
    72.     int x;
    73.     for (x = 0; x < COLS; ++x) {
    74.         int delta = (rand() % 3) - 1;  /* -1, 0, +1 */
    75.         heights[x] += delta;
    76.  
    77.         if (heights[x] < min_h) heights[x] = min_h;
    78.         if (heights[x] > max_h) heights[x] = max_h;
    79.     }
    80. }
    81.  
    82. int main(void) {
    83.     int heights[COLS];
    84.     int x;
    85.  
    86.     srand((unsigned int)time(NULL));
    87.  
    88.     /* Инициализируем высоты: 4–11 */
    89.     for (x = 0; x < COLS; ++x) {
    90.         heights[x] = 4 + (rand() % 8);
    91.     }
    92.  
    93.     while (1) {
    94.         draw_fire(heights);
    95.         update_heights(heights, 3, 14);
    96.         usleep(DELAY_US);
    97.     }
    98.     return 0;
    99. }
    100.  
     
  20. GRAFik

    GRAFik Active Member

    Публикаций:
    0
    Регистрация:
    14 мар 2020
    Сообщения:
    525
    Код (C):
    1. // ======  Код на СИ для VS 2022 Pro  =========================================
    2.  
    3. /* Костёр в терминале (динамический размер). */
    4.  
    5. #include <windows.h>
    6. #include <stdio.h>
    7. #include <stdlib.h>
    8. #include <time.h>
    9.  
    10. #define DELAY_MS 40  // задержка в миллисекундах (~25 FPS)
    11.  
    12. static const char flame_chars[] = "^*.:";
    13. HANDLE hConsole;
    14. WORD defaultAttributes;
    15.  
    16. /* Цвет по позиции внутри «языка огня»:
    17.    нижняя половина — красный, верхняя — жёлтый. */
    18. WORD color_attributes(int y_from_bottom, int max_height) {
    19.     double ratio = (double)y_from_bottom / (double)(max_height ? max_height : 1);
    20.     if (ratio < 0.5)
    21.         return FOREGROUND_RED | FOREGROUND_INTENSITY;                  // ярко-красный
    22.     return FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY; // ярко-жёлтый
    23. }
    24.  
    25. /* Рисует кадр огня по массиву высот. */
    26. void draw_fire(int* heights, int rows, int cols) {
    27.     // Переместим курсор в верхний левый угол консоли (0,0).
    28.     COORD cursorPos = {0, 0};
    29.     SetConsoleCursorPosition(hConsole, cursorPos);
    30.  
    31.     for (int y = 0; y < rows; ++y) {
    32.         for (int x = 0; x < cols; ++x) {
    33.             int h = heights[x];
    34.             // Если «пламя» отсутствует на этом столбце:
    35.             if (h <= 0) {
    36.                 if (y == rows - 1) {
    37.                     // Самая нижняя строка: «угли»
    38.                     SetConsoleTextAttribute(hConsole, FOREGROUND_RED | FOREGROUND_INTENSITY);
    39.                     putchar('#');
    40.                 } else {
    41.                     putchar(' ');
    42.                 }
    43.                 continue;
    44.             }
    45.             int bottom_row = rows - 1;
    46.             int top_row = rows - h;
    47.             if (y < top_row || y > bottom_row) {
    48.                 putchar(' ');
    49.                 continue;
    50.             }
    51.             int y_from_bottom = bottom_row - y;
    52.             // Выбираем атрибут цвета
    53.             WORD color = color_attributes(y_from_bottom, h);
    54.             SetConsoleTextAttribute(hConsole, color);
    55.             // Случайный символ огня
    56.             char ch = flame_chars[rand() % (sizeof(flame_chars) - 1)];
    57.             putchar(ch);
    58.         }
    59.         // Не выводим перенос строки на самом последнем ряду,
    60.         // чтобы консоль не скроллилась вниз и не ломала кадр.
    61.         if (y < rows - 1) {
    62.             putchar('\n');
    63.         }
    64.     }
    65.     // Восстановим исходный атрибут цвета
    66.     SetConsoleTextAttribute(hConsole, defaultAttributes);
    67.     fflush(stdout);
    68. }
    69.  
    70. /* Плавное изменение высот «языков» (колебания). */
    71. void update_heights(int* heights, int cols, int min_h, int max_h) {
    72.     for (int x = 0; x < cols; ++x) {
    73.         int delta = (rand() % 3) - 1;  // -1, 0 или +1
    74.         heights[x] += delta;
    75.         if (heights[x] < min_h) heights[x] = min_h;
    76.         if (heights[x] > max_h) heights[x] = max_h;
    77.     }
    78. }
    79.  
    80. int main(void) {
    81.     // Инициализация генератора случайных чисел
    82.     srand((unsigned int)time(NULL));
    83.  
    84.     // Получаем дескриптор консоли
    85.     hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
    86.     if (hConsole == INVALID_HANDLE_VALUE) return 1;
    87.  
    88.     // 1. Желаемый размер окна (Ширина x Высота в символах)
    89.     int cols = 100;
    90.     int rows = 35;
    91.  
    92.     // Устанавливаем размер буфера и самого окна консоли
    93.     COORD coord = { (SHORT)cols, (SHORT)rows };
    94.     SMALL_RECT rect = { 0, 0, (SHORT)(cols - 1), (SHORT)(rows - 1) };
    95.    
    96.     // Сначала меняем размер буфера, затем окна (или наоборот, если уменьшаем)
    97.     SetConsoleScreenBufferSize(hConsole, coord);
    98.     SetConsoleWindowInfo(hConsole, TRUE, &rect);
    99.  
    100.     // Запрашиваем финальные размеры (на случай, если ОС скорректировала их под экран)
    101.     CONSOLE_SCREEN_BUFFER_INFO csbi;
    102.     if (GetConsoleScreenBufferInfo(hConsole, &csbi)) {
    103.         defaultAttributes = csbi.wAttributes;
    104.         cols = csbi.srWindow.Right - csbi.srWindow.Left + 1;
    105.         rows = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
    106.     } else {
    107.         return 1;
    108.     }
    109.  
    110.     // 2. Скрываем мигающий курсор, чтобы не портить визуал
    111.     CONSOLE_CURSOR_INFO cursorInfo;
    112.     GetConsoleCursorInfo(hConsole, &cursorInfo);
    113.     cursorInfo.bVisible = FALSE;
    114.     SetConsoleCursorInfo(hConsole, &cursorInfo);
    115.  
    116.     // Динамически выделяем память под массив высот (совместимо с x64 в VS)
    117.     int* heights = (int*)malloc(cols * sizeof(int));
    118.     if (heights == NULL) return 1;
    119.  
    120.     // Рассчитываем адекватные пропорции пламени относительно высоты окна
    121.     int min_fire_height = rows / 5;       // Минимальная высота пламени
    122.     int max_fire_height = (rows * 4) / 5; // Максимальная высота пламени
    123.  
    124.     // Начальные случайные высоты
    125.     for (int x = 0; x < cols; ++x) {
    126.         heights[x] = min_fire_height + (rand() % (max_fire_height - min_fire_height + 1));
    127.     }
    128.  
    129.     // Основной цикл: рисуем и обновляем высоты
    130.     while (1) {
    131.         draw_fire(heights, rows, cols);
    132.         update_heights(heights, cols, min_fire_height, max_fire_height);
    133.         Sleep(DELAY_MS);
    134.     }
    135.  
    136.     // Освобождаем ресурсы (хотя цикл бесконечный, для порядка)
    137.     free(heights);
    138.     return 0;
    139. }
     
    dcc0 нравится это.