LINUX.ORG.RU

Нейронные сети. Последний тест. Сеть написала 3d Raycasting с ascii

 


1

1

Мне кажется, удачно получилось у deepai.org написать 3D Raycasting с библиотекой ncurses. Можно двигаться стрелками. Стены символами отрисованы. Во всяком случае у меня работает:

Компиляция и запуск

gcc -o raycast raycast.c -lncurses -ltinfo  -lm; ./raycast
/*Модель 3D Raycasting на C89 с библиотекой ncurses. Написана нейронной сетью.
Стены с помощью символов
Компиляция и запуск gcc -o ray14 ray14.c -lncurses -ltinfo  -lm; ./ ray14
*/
#include <ncurses.h>
#include <math.h>
#include <stdlib.h>

#define MAP_WIDTH 16
#define MAP_HEIGHT 16
#define FOV (M_PI / 3.0) // 60 градусов
#define MAX_DEPTH 16

// Карта: 0 - пусто, 1 - стена
int worldMap[MAP_HEIGHT][MAP_WIDTH] = {
    {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
    {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
    {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
    {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
    {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
    {1,0,0,0,0,1,1,1,1,1,0,0,0,0,0,1},
    {1,0,0,0,0,1,0,0,0,1,0,0,0,0,0,1},
    {1,0,0,0,0,1,0,0,0,1,0,0,0,0,0,1},
    {1,0,0,0,0,1,0,0,0,1,0,0,0,0,0,1},
    {1,0,0,0,0,1,1,1,1,1,0,0,0,0,0,1},
    {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
    {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
    {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
    {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}
};

double playerX = 3.0, playerY = 3.0; // Начальные координаты игрока
double playerAngle = 0.0;

void draw(int width, int height) {
    for (int x = 0; x < width; x++) {
        // Вычисляем угол для каждого луча
        double rayAngle = (playerAngle - FOV / 2.0) + ((double)x / (double)width) * FOV;
        double distanceToWall = 0;
        int hit = 0;

        double eyeX = cos(rayAngle);
        double eyeY = sin(rayAngle);

        while (!hit && distanceToWall < MAX_DEPTH) {
            distanceToWall += 0.1;
            int testX = (int)(playerX + eyeX * distanceToWall);
            int testY = (int)(playerY + eyeY * distanceToWall);

            // Проверка выхода за границы
            if (testX < 0 || testX >= MAP_WIDTH || testY < 0 || testY >= MAP_HEIGHT) {
                hit = 1;
                distanceToWall = MAX_DEPTH;
            } else {
                if (worldMap[testY][testX] == 1) {
                    hit = 1;
                }
            }
        }

        // Рассчитаем высоту стен
        int ceiling = (int)((double)(height / 2.0) - height / distanceToWall);
        int floor = height - ceiling;

        for (int y = 0; y < height; y++) {
            if (y < ceiling) {
                mvaddch(y, x, ' ');
            } else if (y > ceiling && y <= floor) {
                // Можно добавить тень
                if (distanceToWall <= MAX_DEPTH / 4.0) {
                    mvaddch(y, x, '#'); // Ближе - темнее
                } else if (distanceToWall < MAX_DEPTH / 2.0) {
                    mvaddch(y, x, '+');
                } else {
                    mvaddch(y, x, '.');
                }
            } else {
                mvaddch(y, x, ' ');
            }
        }
    }
    refresh();
}

int main() {
    initscr();
    noecho();
    curs_set(FALSE);
    keypad(stdscr, TRUE);
    nodelay(stdscr, TRUE); // Не блокировать getch

    int width, height;
    getmaxyx(stdscr, height, width);

    while (1) {
        // Обработка ввода
        int ch = getch();
        if (ch == 'q') break; // Выход
        if (ch == KEY_UP) {
            // Вперёд
            double newX = playerX + cos(playerAngle) * 0.1;
            double newY = playerY + sin(playerAngle) * 0.1;
            if (worldMap[(int)newY][(int)newX] == 0) {
                playerX = newX;
                playerY = newY;
            }
        }
        if (ch == KEY_DOWN) {
            // Назад
            double newX = playerX - cos(playerAngle) * 0.1;
            double newY = playerY - sin(playerAngle) * 0.1;
            if (worldMap[(int)newY][(int)newX] == 0) {
                playerX = newX;
                playerY = newY;
            }
        }
        if (ch == KEY_LEFT) {
            playerAngle -= 0.1;
        }
        if (ch == KEY_RIGHT) {
            playerAngle += 0.1;
        }

        draw(width, height);
    }

    endwin();
    return 0;
}

P.S. Всё. Больше не буду топы создавать. Я понял, что вы устали… Но правда, неплохо получилось же?!



Последнее исправление: AnonymUser (всего исправлений: 1)

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

a1ba ★★★★
()

Попросил исправить эффект рыбьего глаза н. сеть deepai.org. Справилась. Т.е. и дня не прошло, а уже выпускаем версию программы 2.0 Doom для терминала без стрельбы и врагов.

/*Модель 3D Raycasting на C89 с библиотекой ncurses. Написана нейронной сетью.
Стены с помощью символов
Компиляция и запуск gcc -o ray14 ray14.c -lncurses -ltinfo  -lm; ./ ray14
Убран эффект рыбьего глаза*/

#include <ncurses.h>
#include <math.h>
#include <stdlib.h>

#define MAP_WIDTH 16
#define MAP_HEIGHT 16
#define FOV (M_PI / 3.0) // 60 градусов
#define MAX_DEPTH 16

// Карта: 0 - пусто, 1 - стена
int worldMap[MAP_HEIGHT][MAP_WIDTH] = {
    {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
    {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
    {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
    {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
    {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
    {1,0,0,0,0,1,1,1,1,1,0,0,0,0,0,1},
    {1,0,0,0,0,1,0,0,0,1,0,0,0,0,0,1},
    {1,0,0,0,0,1,0,0,0,1,0,0,0,0,0,1},
    {1,0,0,0,0,1,0,0,0,1,0,0,0,0,0,1},
    {1,0,0,0,0,1,1,1,1,1,0,0,0,0,0,1},
    {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
    {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
    {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
    {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}
};

double playerX = 3.0, playerY = 3.0; // Начальные координаты игрока
double playerAngle = 0.0;

void draw(int width, int height) {
    for (int x = 0; x < width; x++) {
        // Вычисляем угол для каждого луча
        double rayAngle = (playerAngle - FOV / 2.0) + ((double)x / (double)width) * FOV;
        double distanceToWall = 0;
        int hit = 0;

        double eyeX = cos(rayAngle);
        double eyeY = sin(rayAngle);

        while (!hit && distanceToWall < MAX_DEPTH) {
            distanceToWall += 0.1;
            int testX = (int)(playerX + eyeX * distanceToWall);
            int testY = (int)(playerY + eyeY * distanceToWall);

            // Проверка выхода за границы
            if (testX < 0 || testX >= MAP_WIDTH || testY < 0 || testY >= MAP_HEIGHT) {
                hit = 1;
                distanceToWall = MAX_DEPTH;
            } else {
                if (worldMap[testY][testX] == 1) {
                    hit = 1;
                }
            }
        }

        // new: Корректировка расстояния для устранения эффекта рыбьего глаза (new)
        double angleDifference = rayAngle - playerAngle; // new: расчет разницы углов (new)
        if (angleDifference > M_PI) angleDifference -= 2 * M_PI; // new: приведение к диапазону (-π, π) (new)
        if (angleDifference < -M_PI) angleDifference += 2 * M_PI; // new

        distanceToWall = distanceToWall * cos(angleDifference); // new: исправление расстояния (new)

        // Рассчитаем высоту стен
        int ceiling = (int)((double)(height / 2.0) - height / distanceToWall);
        int floor = height - ceiling;

        for (int y = 0; y < height; y++) {
            if (y < ceiling) {
                mvaddch(y, x, ' ');
            } else if (y > ceiling && y <= floor) {
                // Можно добавить тень
                if (distanceToWall <= MAX_DEPTH / 4.0) {
                    mvaddch(y, x, '#'); // Ближе - темнее
                } else if (distanceToWall < MAX_DEPTH / 2.0) {
                    mvaddch(y, x, '+');
                } else {
                    mvaddch(y, x, '.');
                }
            } else {
                mvaddch(y, x, ' ');
            }
        }
    }
    refresh();
}
int main() {
    initscr();
    noecho();
    curs_set(FALSE);
    keypad(stdscr, TRUE);
    nodelay(stdscr, TRUE); // Не блокировать getch

    int width, height;
    getmaxyx(stdscr, height, width);

    while (1) {
        // Обработка ввода
        int ch = getch();
        if (ch == 'q') break; // Выход
        if (ch == KEY_UP) {
            // Вперёд
            double newX = playerX + cos(playerAngle) * 0.1;
            double newY = playerY + sin(playerAngle) * 0.1;
            if (worldMap[(int)newY][(int)newX] == 0) {
                playerX = newX;
                playerY = newY;
            }
        }
        if (ch == KEY_DOWN) {
            // Назад
            double newX = playerX - cos(playerAngle) * 0.1;
            double newY = playerY - sin(playerAngle) * 0.1;
            if (worldMap[(int)newY][(int)newX] == 0) {
                playerX = newX;
                playerY = newY;
            }
        }
        if (ch == KEY_LEFT) {
            playerAngle -= 0.1;
        }
        if (ch == KEY_RIGHT) {
            playerAngle += 0.1;
        }

        draw(width, height);
    }

    endwin();
    return 0;
}
AnonymUser
() автор топика
Ответ на: комментарий от Bad_ptr

Дальше можно использовать ии для объяснения отдельных участков кода. Но в общем дальше уже не сильно интересно.

AnonymUser
() автор топика
Ответ на: удаленный комментарий

В общем, наверное, да. Скажем так: сделала на основе существующих паттернов (образцов). «Написала» при помощи компилятивного метода.

AnonymUser
() автор топика
Последнее исправление: AnonymUser (всего исправлений: 1)

Вариант с minimap и направлением взгляда.

Подоспел вариант с миникартой и направлением взгляда:

/*Модель 3D Raycasting на C89 с библиотекой ncurses. Написана нейронной сетью.
Стены с помощью символов
Компиляция и запуск gcc -o ray14 ray14.c -lncurses -ltinfo  -lm; ./ray14
Убран эффект рыбьего глаза*/
#include <ncurses.h>
#include <math.h>
#include <stdlib.h>

#define MAP_WIDTH 16
#define MAP_HEIGHT 16
#define FOV (M_PI / 3.0) // 60 градусов
#define MAX_DEPTH 16

// Карта: 0 - пусто, 1 - стена
int worldMap[MAP_HEIGHT][MAP_WIDTH] = {
    {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
    {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
    {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
    {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
    {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
    {1,0,0,0,0,1,1,1,1,1,0,0,0,0,0,1},
    {1,0,0,0,0,1,0,0,0,1,0,0,0,0,0,1},
    {1,0,0,0,0,1,0,0,0,1,0,0,0,0,0,1},
    {1,0,0,0,0,1,0,0,0,1,0,0,0,0,0,1},
    {1,0,0,0,0,1,1,1,1,1,0,0,0,0,0,1},
    {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
    {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
    {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
    {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}
};

double playerX = 3.0, playerY = 3.0; // Начальные координаты игрока
double playerAngle = 0.0;

// Функция для отрисовки мини-карты
void drawMiniMap(int mapX, int mapY, int mapSize) {
    for (int y = 0; y < MAP_HEIGHT; y++) {
        for (int x = 0; x < MAP_WIDTH; x++) {
            char ch = (worldMap[y][x] == 1) ? '#' : ' ';
            mvaddch(mapY + y, mapX + x, ch);
        }
    }
    // Отметка положения игрока
    mvaddch(mapY + (int)playerY, mapX + (int)playerX, '@');

    // Добавляем линию направления взгляда
    double dirX = cos(playerAngle);
    double dirY = sin(playerAngle);
    int lineXEnd = (int)(playerX + dirX * 3);
    int lineYEnd = (int)(playerY + dirY * 3);

    // Рисуем линию направления (используем Bresenham или простую)
    int x1 = (int)playerX;
    int y1 = (int)playerY;
    int x2 = lineXEnd;
    int y2 = lineYEnd;

    int dx = abs(x2 - x1), sx = x1 < x2 ? 1 : -1;
    int dy = -abs(y2 - y1), sy = y1 < y2 ? 1 : -1; 
    int err = dx + dy, e2;

    while (1) {
        mvaddch(mapY + y1, mapX + x1, '*'); // или другой символ
        if (x1 == x2 && y1 == y2) break;
        e2 = 2 * err;
        if (e2 >= dy) { err += dy; x1 += sx; }
        if (e2 <= dx) { err += dx; y1 += sy; }
    }
}

void draw(int width, int height) {
    for (int x = 0; x < width; x++) {
        // Исправленная строка
        double rayAngle = (playerAngle - FOV / 2.0) + ((double)x / (double)width) * FOV;
        double distanceToWall = 0;
        int hit = 0;

        double eyeX = cos(rayAngle);
        double eyeY = sin(rayAngle);

        while (!hit && distanceToWall < MAX_DEPTH) {
            distanceToWall += 0.1;
            int testX = (int)(playerX + eyeX * distanceToWall);
            int testY = (int)(playerY + eyeY * distanceToWall);

            if (testX < 0 || testX >= MAP_WIDTH || testY < 0 || testY >= MAP_HEIGHT) {
                hit = 1;
                distanceToWall = MAX_DEPTH;
            } else {
                if (worldMap[testY][testX] == 1) {
                    hit = 1;
                }
            }
        }

        double angleDifference = rayAngle - playerAngle;
        if (angleDifference > M_PI) angleDifference -= 2 * M_PI;
        if (angleDifference < -M_PI) angleDifference += 2 * M_PI;

        distanceToWall = distanceToWall * cos(angleDifference);

        int ceiling = (int)((double)(height / 2.0) - height / distanceToWall);
        int floor = height - ceiling;

        for (int y = 0; y < height; y++) {
            if (y < ceiling) {
                mvaddch(y, x, ' ');
            } else if (y > ceiling && y <= floor) {
                if (distanceToWall <= MAX_DEPTH / 4.0) {
                    mvaddch(y, x, '#');
                } else if (distanceToWall < MAX_DEPTH / 2.0) {
                    mvaddch(y, x, '+');
                } else {
                    mvaddch(y, x, '.');
                }
            } else {
                mvaddch(y, x, ' ');
            }
        }
    }
    refresh();
}

int main() {
    initscr();
    noecho();
    curs_set(FALSE);
    keypad(stdscr, TRUE);
    nodelay(stdscr, TRUE);

    int width, height;
    getmaxyx(stdscr, height, width);

    // Позиция мини-карты в левом нижнем углу
    int miniMapSizeX = 16;
    int miniMapSizeY = 16;
    int miniMapPosX = 1;
    int miniMapPosY = height - miniMapSizeY - 1; // снизу

    while (1) {
        int ch = getch();
        if (ch == 'q') break;
        if (ch == KEY_UP) {
            double newX = playerX + cos(playerAngle) * 0.1;
            double newY = playerY + sin(playerAngle) * 0.1;
            if (worldMap[(int)newY][(int)newX] == 0) {
                playerX = newX;
                playerY = newY;
            }
        }
        if (ch == KEY_DOWN) {
            double newX = playerX - cos(playerAngle) * 0.1;
            double newY = playerY - sin(playerAngle) * 0.1;
            if (worldMap[(int)newY][(int)newX] == 0) {
                playerX = newX;
                playerY = newY;
            }
        }
        if (ch == KEY_LEFT) {
            playerAngle -= 0.1;
        }
        if (ch == KEY_RIGHT) {
            playerAngle += 0.1;
        }

        draw(width, height);
        drawMiniMap(miniMapPosX, miniMapPosY, miniMapSizeX);
    }

    endwin();
    return 0;
}

Интересно, что на каком-то запросе сеть сделал опечатку - в переменную добавила кириллический символ. С такими ошибками ещё не встречался.

AnonymUser
() автор топика
Последнее исправление: AnonymUser (всего исправлений: 1)

C89 без ncurses. Т.е. вообще везде должно работать.

gcc -std=c89  -o ray ray.c   -lm; ./ray



/*Модель 3D Raycasting на C(89). Написана нейронной сетью.
Стены с помощью символов
Компиляция и запуск gcc -o ray ray.c   -lm; ./ray
* или gcc -std=c89  -o ray ray.c   -lm; ./ray
Убран эффект рыбьего глаза*/
#include <stdio.h>
#include <math.h>
#include <string.h>

/*Размеры карты и экрана*/
#define MAP_WIDTH 16
#define MAP_HEIGHT 16
#define SCREEN_WIDTH 80
#define SCREEN_HEIGHT 24
#define M_PI 3.14159265358979323846
#define FOV (M_PI / 3.0)
#define MAX_DEPTH 16.0

/*Карта*/
int worldMap[MAP_HEIGHT][MAP_WIDTH] = {
    {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
    {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
    {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
    {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
    {1,0,0,0,1,1,1,0,0,1,1,1,0,0,0,1},
    {1,0,0,0,1,0,1,0,0,1,0,1,0,0,0,1},
    {1,0,0,0,1,1,1,0,0,1,1,1,0,0,0,1},
    {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
    {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
    {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
    {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
    {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
    {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
    {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
    {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
    {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}
};

double playerX = 3.0;  /*Начальные координаты игрока*/
double playerY = 3.0;
double playerAngle = 0.0;

void draw() {
	int y, x, testX, testY, hit, ceiling, floor;
	double angleDifference;
	    double eyeX, eyeY, rayAngle, distanceToWall;
    printf("\033[2J");
    printf("\033[H");
    fflush(stdout);

    for (y = 0; y < SCREEN_HEIGHT; y++) {
        for (x = 0; x < SCREEN_WIDTH; x++) {
			  /*Вычисляем угол для каждого луча*/
            rayAngle = (playerAngle - FOV / 2.0) + ((double)x / (double)SCREEN_WIDTH) * FOV;
            distanceToWall = 0.0;
             hit = 0;

            eyeX = cos(rayAngle);
            eyeY = sin(rayAngle);

            while (!hit && distanceToWall < MAX_DEPTH) {
                distanceToWall += 0.1;  /* шаг по лучу*/
                 testX = (int)(playerX + eyeX * distanceToWall); /*переменная луча по X*/
                 testY = (int)(playerY + eyeY * distanceToWall); /*по Y*/

                if (testX < 0 || testX >= MAP_WIDTH || testY < 0 || testY >= MAP_HEIGHT) {
                    hit = 1;  /*луч вышел за границы карты, считаем, что столкнулся с границей*/
                    distanceToWall = MAX_DEPTH;
                } else {
                    if (worldMap[testY][testX] == 1) {
                        hit = 1; /*столкновение со стеной*/
                    }
                }
            }

			/*Корректировка расстояния для устранения эффекта рыбьего глаза*/
			 angleDifference = rayAngle - playerAngle;
			if (angleDifference > M_PI) angleDifference -= 2 * M_PI;
			if (angleDifference < -M_PI) angleDifference += 2 * M_PI;

            ceiling = (int)((SCREEN_HEIGHT / 2.0) - SCREEN_HEIGHT / distanceToWall);
            floor = SCREEN_HEIGHT - ceiling;

            if (y < ceiling) {
                printf(" "); /*рисуем пробел - небо*/
            } else if (y > ceiling && y <= floor) {
                if (distanceToWall <= MAX_DEPTH / 4.0) {
                    printf("#"); /*рисуем стены разной жирности*/
                } else if (distanceToWall < MAX_DEPTH / 2.0) {
                    printf("+");
                } else {
                    printf(".");
                }
            } else {
                printf(" "); /*рисуем пол*/
            }
        }
        printf("\n");
    }
}

int main() {
    char command[10];
    double newX,  newY;
    char c;

    while (1) {
        draw();
        printf("Введите команду (W/A/S/D для движения, 'exit' для выхода): ");
        if (!fgets(command, sizeof(command), stdin)) break;

        if (strncmp(command, "exit", 4) == 0) {
            break; /*Выход из программы*/
        }

        c = command[0];
        if (c == 'w' || c == 'W') {
            /*Вперед*/
             newX = playerX + cos(playerAngle) * 0.2;
             newY = playerY + sin(playerAngle) * 0.2;
            if (worldMap[(int)newY][(int)newX] == 0) {
                playerX = newX;
                playerY = newY;
            }
        } else if (c == 's' || c == 'S') {
            /*Назад*/
            newX = playerX - cos(playerAngle) * 0.2;
            newY = playerY - sin(playerAngle) * 0.2;
            if (worldMap[(int)newY][(int)newX] == 0) {
                playerX = newX;
                playerY = newY;
            }
        } else if (c == 'a' || c == 'A') {
            /*Поворот налево*/
            playerAngle -= 0.1;
        } else if (c == 'd' || c == 'D') {
            /*Поворот направо*/
            playerAngle += 0.1;
        }
    }
    return 0;
}

AnonymUser
() автор топика

В стиле матрицы:

/*Модель 3D Raycasting на C(89). Написана нейронной сетью. Отредактирована by dcc0.
Стены с помощью символов
Компиляция и запуск gcc -o ray ray.c   -lm; ./ray
* или gcc -std=c89  -o ray ray.c   -lm; ./ray
Убран эффект рыбьего глаза*/
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
/*Размеры карты и экрана*/
#define MAP_WIDTH 16
#define MAP_HEIGHT 16
#define SCREEN_WIDTH 80
#define SCREEN_HEIGHT 24
#define M_PI 3.14159265358979323846
#define FOV (M_PI / 3.0)
#define MAX_DEPTH 16.0

/*Карта*/
int worldMap[MAP_HEIGHT][MAP_WIDTH] = {
    {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
    {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
    {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
    {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
    {1,0,0,0,1,1,1,0,0,1,1,1,0,0,0,1},
    {1,0,0,0,1,0,1,0,0,1,0,1,0,0,0,1},
    {1,0,0,0,1,1,1,0,0,1,1,1,0,0,0,1},
    {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
    {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
    {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
    {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
    {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
    {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
    {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
    {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
    {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}
};

double playerX = 3.0;  /*Начальные координаты игрока*/
double playerY = 3.0;
double playerAngle = 0.0;

void draw() {
	int y, x, RayX, RayY, hit, ceiling, floor, rrandd;
	double angleDifference;
	double eyeX, eyeY, rayAngle, distanceToWall;
    printf("\033[2J"); //Очистка экрана
    printf("\033[H"); //И сброс курсора в начало
    fflush(stdout);

    for (y = 0; y < SCREEN_HEIGHT; y++) {
        for (x = 0; x < SCREEN_WIDTH; x++) {
			  /*Вычисляем угол для каждого луча*/
            rayAngle = (playerAngle - FOV / 2.0) + ((double)x / (double)SCREEN_WIDTH) * FOV;
            distanceToWall = 0.0;
             hit = 0;
             /*Можно удалить */
            //eyeX = cos(rayAngle);
            //eyeY = sin(rayAngle);
            while (!hit && distanceToWall < MAX_DEPTH) {
                distanceToWall += 0.1;  /* шаг по лучу*/
                // testX = (int)(playerX + eyeX * distanceToWall); /*переменная луча по X*/
                 //testY = (int)(playerY + eyeY * distanceToWall); /*по Y*/
                RayX = (int)(playerX + cos(rayAngle) * distanceToWall); /*переменная луча по X*/
                RayY = (int)(playerY + sin(rayAngle) * distanceToWall); /*по Y*/

                if (RayX < 0 || RayX >= MAP_WIDTH || RayY < 0 || RayY >= MAP_HEIGHT) {
                    hit = 1;  /*луч вышел за границы карты, считаем, что столкнулся с границей*/
                    distanceToWall = MAX_DEPTH;
                } else {
                    if (worldMap[RayY][RayX] == 1) {
                        hit = 1; /*столкновение со стеной*/
                    }
                }
            }

			/*Корректировка расстояния для устранения эффекта рыбьего глаза*/
			 angleDifference = rayAngle - playerAngle;
			if (angleDifference > M_PI) angleDifference -= 2 * M_PI;
			if (angleDifference < -M_PI) angleDifference += 2 * M_PI;

            ceiling = (int)((SCREEN_HEIGHT / 2.0) - SCREEN_HEIGHT / distanceToWall);
            floor = SCREEN_HEIGHT - ceiling;

             srand(time(NULL));
			 rrandd = (rand() %10);

            if (y < ceiling) {
                printf(" "); /*рисуем пробел - небо*/
            } else if (y > ceiling && y <= floor) {
                if (distanceToWall <= MAX_DEPTH / 4.0) {
					printf("\033[0;32m");
                    printf("%d",  rrandd ); /*рисуем стены разной жирности*/
                } else if (distanceToWall < MAX_DEPTH / 2.0) {
                    printf("+");
                } else {
                    printf(".");
                }
            } else {
				printf("\033[0;32m");
                printf(" "); /*рисуем пол*/
            }
        }
        printf("\n");
    }
}

int main() {
    char command[10];
    double newX,  newY;
    char c;
//Управление
    while (1) {
		//На каждое движение перерисовываем видимую область
        draw();
        printf("Введите  (W/A/S/D + enter, 'exit'  - выход): ");
        if (!fgets(command, sizeof(command), stdin)) break;

        if (strncmp(command, "exit", 4) == 0) {
            break; /*Выход из программы*/
        }

        c = command[0];
        if (c == 'w' || c == 'W') {
            /*Вперед*/
             newX = playerX + cos(playerAngle) * 0.2;
             newY = playerY + sin(playerAngle) * 0.2;
            if (worldMap[(int)newY][(int)newX] == 0) {
                playerX = newX;
                playerY = newY;
            }
        } else if (c == 's' || c == 'S') {
            /*Назад*/
            newX = playerX - cos(playerAngle) * 0.2;
            newY = playerY - sin(playerAngle) * 0.2;
            if (worldMap[(int)newY][(int)newX] == 0) {
                playerX = newX;
                playerY = newY;
            }
        } else if (c == 'a' || c == 'A') {
            /*Поворот налево*/
            playerAngle -= 0.1;
        } else if (c == 'd' || c == 'D') {
            /*Поворот направо*/
            playerAngle += 0.1;
        }
    }
    return 0;
}

AnonymUser
() автор топика

На PHP:

<?php
//Конвертирован из кода C89 нейронной сетью. 
session_start();

// Размеры карты и параметры
define('MAP_WIDTH', 16);
define('MAP_HEIGHT', 16);
define('SCREEN_WIDTH', 60);
define('SCREEN_HEIGHT', 24);
define('FOV', M_PI / 3.0);
define('MAX_DEPTH', 16.0);

// Карта
$worldMap = [
    [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
    [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
    [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
    [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
    [1,0,0,0,1,1,1,0,0,1,1,1,0,0,0,1],
    [1,0,0,0,1,0,1,0,0,1,0,1,0,0,0,1],
    [1,0,0,0,1,1,1,0,0,1,1,1,0,0,0,1],
    [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
    [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
    [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
    [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
    [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
    [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
    [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
    [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
    [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]
];

// Инициализация или получение состояния игрока
if (!isset($_SESSION['playerX'])) {
    $_SESSION['playerX'] = 3.0;
    $_SESSION['playerY'] = 3.0;
    $_SESSION['playerAngle'] = 0.0;
}

// Обработка команд
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $command = $_POST['command'] ?? '';

    $playerX = &$_SESSION['playerX'];
    $playerY = &$_SESSION['playerY'];
    $playerAngle = &$_SESSION['playerAngle'];

    $newX = $playerX;
    $newY = $playerY;

    switch ($command) {
        case 'W':
        case 'w':
            $newX = $playerX + cos($playerAngle) * 0.2;
            $newY = $playerY + sin($playerAngle) * 0.2;
            if ($worldMap[(int)$newY][(int)$newX] === 0) {
                $playerX = $newX;
                $playerY = $newY;
            }
            break;
        case 'S':
        case 's':
            $newX = $playerX - cos($playerAngle) * 0.2;
            $newY = $playerY - sin($playerAngle) * 0.2;
            if ($worldMap[(int)$newY][(int)$newX] === 0) {
                $playerX = $newX;
                $playerY = $newY;
            }
            break;
        case 'A':
        case 'a':
            $playerAngle -= 0.1;
            break;
        case 'D':
        case 'd':
            $playerAngle += 0.1;
            break;
    }
}

// Функция рисования
function draw() {
    global $worldMap, $_SESSION;

    $playerX = $_SESSION['playerX'];
    $playerY = $_SESSION['playerY'];
    $playerAngle = $_SESSION['playerAngle'];

    $output = "3d"; // Очистка терминала
    for ($y = 0; $y < SCREEN_HEIGHT; $y++) {
        for ($x = 0; $x < SCREEN_WIDTH; $x++) {
            $rayAngle = ($playerAngle - FOV / 2) + (($x / SCREEN_WIDTH) * FOV);
            $distanceToWall = 0.0;
            $hit = false;

            while (!$hit && $distanceToWall < MAX_DEPTH) {
                $distanceToWall += 0.1;
                $rayX = (int)($playerX + cos($rayAngle) * $distanceToWall);
                $rayY = (int)($playerY + sin($rayAngle) * $distanceToWall);

                if ($rayX < 0 || $rayX >= MAP_WIDTH || $rayY < 0 || $rayY >= MAP_HEIGHT) {
                    $hit = true;
                    $distanceToWall = MAX_DEPTH;
                } else {
                    if ($worldMap[$rayY][$rayX] == 1) {
                        $hit = true;
                    }
                }
            }

            $angleDiff = $rayAngle - $playerAngle;
            if ($angleDiff > M_PI) $angleDiff -= 2 * M_PI;
            if ($angleDiff < -M_PI) $angleDiff += 2 * M_PI;

            $ceiling = (int)((SCREEN_HEIGHT / 2.0) - SCREEN_HEIGHT / $distanceToWall);
            $floor = SCREEN_HEIGHT - $ceiling;

            if ($y < $ceiling) {
                $output .= " ";
            } elseif ($y > $ceiling && $y <= $floor) {
                if ($distanceToWall <= MAX_DEPTH / 4.0) {
                    $output .= "#";
                } elseif ($distanceToWall < MAX_DEPTH / 2.0) {
                    $output .= "+";
                } else {
                    $output .= ".";
                }
            } else {
                $output .= " ";
            }
        }
        $output .= "\n";
    }
    return $output;
}

?>

<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8" />
<title>3D Raycasting</title>
</head>
<body style="font-family: monospace; white-space: pre;">
<h2>3D Raycasting на PHP</h2>
<pre>
<?php echo draw(); ?>
</pre>
<form method="post">
    <button name="command" value="W">Вперед (W)</button>
    <button name="command" value="S">Назад (S)</button>
    <button name="command" value="A">Поворот налево (A)</button>
    <button name="command" value="D">Поворот направо (D)</button>
    <button type="submit" name="command" value="exit">Выход</button>
</form>
</body>
</html>
AnonymUser
() автор топика
Ответ на: комментарий от AnonymUser

Вот скажите мне пожайлуста, я что такой деревянный?

Мне по сути ИИ помогает в 1 случае из 10. Это хорошо когда я понимаю когда он тупо обманывает, я знаю 100% факт а он пишет наоборот.

Хуже всего когда уводит в сторону. На днях нужно было решить одну задачу, js динамически по данным выдает дашбоард в графане. Внезапно этот жс перестал работать. 2 часа пытал ИИ, делал то что он, хотел, изменял код. Пытался ему сказать что может быть нужно подойти с другой строны.

Короче плюнул и решил задачу сам.

anonymous
()
Ответ на: комментарий от LightDiver

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

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

По ф12 лазал по браузеру …

anonymous
()
Ответ на: комментарий от LightDiver

Причем сама графана работает как часы. И даже руками набросал такой дашбоард в статике тоже как часы.

anonymous
()
Ответ на: комментарий от anonymous

Вы часто видете надпись от ИИ, когда вы после первого вопроса, пишите ему что он не прав а в ответ:

Вы абсолютно правы, извиняюсь за путаницу

У меня это фигня постоянна :(

anonymous
()
Ответ на: комментарий от vazgen05

Судя по результатам большей части моделей - больше.

Но вообще мой пойнт в том, что бенчмаркить режим, которым в реальности никто не станет пользоваться для программирования (все выставляют high/xhigh), немного не репрезентативно.

KivApple ★★★★★
()
Ответ на: комментарий от KivApple

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

vazgen05 ★★★
()
Ответ на: комментарий от anonymous

Короче говоря: если искомая модель у него не описана и даже нет ничего близкого, то, видимо, ии пишет что-то ближайшее к запросу. А это ближайшее с его точки зрения может оказаться в реальности далёким от задачи.

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

Т.е. чем более общий запрос, тем более общий образец и пытается предложить.

Я попросил его написать рейкастинг с ncurses, а затем только отдельно попросил переписать отрисовку и указал, как ее реализовать… А потом убрать ncurses. И вуаля, то, что он не мог написать общим запросом, получилось…

А потом попросил конвертировать код C в php и управление написать через input. Но тут и ежу понятно, что это довольно просто.

AnonymUser
() автор топика
Последнее исправление: AnonymUser (всего исправлений: 2)
Ответ на: комментарий от anonymous

Это зависит от того какая модель ИИ. Opus в целом может дискутировать и я не всегда оказываюсь прав (но «вы абсолютно правы» тоже бывает). Fable вообще не припомню, чтобы слишком глупо ошибался (местами приходится поправлять, но это больше стратегические решения, которые не прописаны чётко в ТЗ, потому что я сам не знал, что возникнет этот вопрос, пока мы не дошли до этого этапа).

Те кто жалуется на ИИ любит не указывать какую именно модель использует. А разница между моделями может быть как между Феррари и Запорожцем.

KivApple ★★★★★
()
Последнее исправление: KivApple (всего исправлений: 2)
Ответ на: комментарий от KivApple

Дело в том, что если у ИИ нет образцов в принципе для задачи и брать их неоткуда, то в общем и генерировать не на чем… Нет подкормки, нет и плодов.

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

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

AnonymUser
() автор топика
Последнее исправление: AnonymUser (всего исправлений: 1)
Ответ на: комментарий от anonymous

Вот момент. Для кода с ncurses прошу исправить эффект рыбьего глаза. Сеть пишет такой код:

	angleDifference = rayAngle - playerAngle; // new: расчет разницы углов (new)
        if (angleDifference > M_PI) angleDifference -= 2 * M_PI; // new: приведение к диапазону (-π, π) (new)
        if (angleDifference < -M_PI) angleDifference += 2 * M_PI; // new
        distanceToWall = distanceToWall * cos(angleDifference); // new: исправление расстояния (new)

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

        distanceToWall = distanceToWall * cos(angleDifference); // new: исправление расстояния (new)

Я даже и не заметил сразу… Т.е. даже в целом удачный вариант надо мониторить построчно.

AnonymUser
() автор топика
Последнее исправление: AnonymUser (всего исправлений: 1)

Кстати, для общего понимания смысла, код можно упростить. Упростить вращение, а соотвественно вычисление playerangle. И вычисление угла луча можно сократить. В случае с ASCII можно и коррекцию рыбьего глаза убрать. И код все равно будет работать:

<?php
session_start();

// Размеры карты и параметры
define('MAP_WIDTH', 16);
define('MAP_HEIGHT', 16);
define('SCREEN_WIDTH', 60);
define ('M_PI', 3.14159265358979323846);
define('SCREEN_HEIGHT', 24);
define('FOV', M_PI / 3.0);
define('MAX_DEPTH', 16.0);

// Карта
$worldMap = [
    [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
    [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
    [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
    [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
    [1,0,0,0,1,1,1,0,0,1,1,1,0,0,0,1],
    [1,0,0,0,1,0,1,0,0,1,0,1,0,0,0,1],
    [1,0,0,0,1,1,1,0,0,1,1,1,0,0,0,1],
    [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
    [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
    [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
    [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
    [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
    [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
    [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
    [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
    [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]
];
//Добавим матрицу вращения вместо бесконечного инкремента
$matrix_angle = [-8, -7.5, -7, -6.5, -6, -5.5, -5, -4.5, -4, -3.5, -3, -2.5, -2,
-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];

// Инициализация или получение состояния игрока
if (!isset($_SESSION['playerX'])) {
	$_SESSION['pa'] = 16;	
    $_SESSION['playerX'] = 3.0;
    $_SESSION['playerY'] = 3.0;
    $_SESSION['playerAngle'] = $matrix_angle[16];

}

// Обработка команд
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $command = $_POST['command'] ?? '';

    $playerX = &$_SESSION['playerX'];
    $playerY = &$_SESSION['playerY'];
    $playerAngle = &$_SESSION['playerAngle'];
    $pa = &$_SESSION['pa'];

    $newX = $playerX;
    $newY = $playerY;


    switch ($command) {
        case 'W':
        case 'w':
            $newX = $playerX + cos($playerAngle) * 0.2;
            $newY = $playerY + sin($playerAngle) * 0.2;
            if ($worldMap[(int)$newY][(int)$newX] === 0) {
                $playerX = $newX;
                $playerY = $newY;
            }
            break;
        case 'S':
        case 's':
            $newX = $playerX - cos($playerAngle) * 0.2;
            $newY = $playerY - sin($playerAngle) * 0.2;
            if ($worldMap[(int)$newY][(int)$newX] === 0) {
                $playerX = $newX;
                $playerY = $newY;
            }
            break;
        case 'A':
        case 'a':
	if ($pa < 0)
		$pa=32;
		$pa-=1;
            $playerAngle = $matrix_angle[$pa];
            break;
        case 'D':
        case 'd':
			$pa+=1;
			if ($pa > 32)
			$pa=0;
            $playerAngle = $matrix_angle[$pa];
            break;
    }
   print "Угол поворта: " . $playerAngle;
}

// Функция рисования
function draw() {
    global $worldMap, $_SESSION;

    $playerX = $_SESSION['playerX'];
    $playerY = $_SESSION['playerY'];
    $playerAngle = $_SESSION['playerAngle'];

    $output = ""; // Очистка терминала
    for ($y = 0; $y < SCREEN_HEIGHT; $y++) {
        for ($x = 0; $x < SCREEN_WIDTH; $x++) {
            //$rayAngle = ($playerAngle - FOV / 2) + (($x / SCREEN_WIDTH) * FOV);
            //Упростим вычисление угла луча.
			$rayAngle = $playerAngle + ($x / SCREEN_WIDTH);
            $distanceToWall = 0.0;
            $hit = false;
            while (!$hit && $distanceToWall < MAX_DEPTH) {
                $distanceToWall += 0.1;
                $rayX = (int)($playerX + cos($rayAngle) * $distanceToWall);
                $rayY = (int)($playerY + sin($rayAngle) * $distanceToWall);
				//Вообще не выполняется, так как RayX или RayY
                //if ($rayX < 0 || $rayX >= MAP_WIDTH || $rayY < 0 || $rayY >= MAP_HEIGHT) {
                   // $hit = true;
                   // $distanceToWall = MAX_DEPTH;
                //} else {
                    if ($worldMap[$rayY][$rayX] == 1) {
                        $hit = true;
                    }
                
            }
			//Закомментируем устранение рыбьего глаза
            //$angleDiff = $rayAngle - $playerAngle;
            //if ($angleDiff > M_PI) $angleDiff -= 2 * M_PI;
            //if ($angleDiff < -M_PI) $angleDiff += 2 * M_PI;
			//$distanceToWall = $distanceToWall * cos($angleDiff); // new: исправление расстояния (new)
            
            $ceiling = (int)((SCREEN_HEIGHT / 2.0) - SCREEN_HEIGHT / $distanceToWall);
            $floor = SCREEN_HEIGHT - $ceiling;
    

            if ($y < $ceiling) {
                $output .= " ";
            } elseif ($y > $ceiling && $y <= $floor) {
                if ($distanceToWall <= MAX_DEPTH / 4.0) {
                    $output .= "#";
                } elseif ($distanceToWall < MAX_DEPTH / 2.0) {
                    $output .= "+";
                } else {
                    $output .= ".";
                }
            } else {
                $output .= " ";
            }
        }
        $output .= "\n";
    }
    return $output;
}

?>

<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8" />
<title>3D Raycasting</title>
</head>
<body style="font-family: monospace; white-space: pre;">
<h2>3D Raycasting на PHP</h2>
<pre>
<?php echo draw(); ?>
</pre>
<form method="post">
    <button name="command" value="W">Вперед (W)</button>
    <button name="command" value="S">Назад (S)</button>
    <button name="command" value="A">Поворот налево (A)</button>
    <button name="command" value="D">Поворот направо (D)</button>
    <button type="submit" name="command" value="exit">Выход</button>
</form>
</body>
</html>

AnonymUser
() автор топика
Ответ на: комментарий от AnonymUser

Тоже на C89. И моя идея верна оказалась. Чтобы увидеть самый примитивный 3d, отсюда можно много выкинуть. Будет не реалистично. Грустано и т.д., но:

rayAngle = (playerAngle - FOV / 2.0) + ((double)x / (double)SCREEN_WIDTH) * FOV;

Даже так оно будет как-то работать:

rayAngle = (playerAngle + (double)x / (double)SCREEN_WIDTH);``
/*Модель 3D Raycasting на C(89). Написана нейронной сетью. Отредактирована by dcc0.
Стены с помощью символов
Компиляция и запуск gcc -o ray ray.c   -lm; ./ray
* или gcc -std=c89  -o ray ray.c   -lm; ./ray
Убран эффект рыбьего глаза*/
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
/*Размеры карты и экрана*/
#define MAP_WIDTH 16
#define MAP_HEIGHT 16
#define SCREEN_WIDTH 60
#define SCREEN_HEIGHT 24
#define M_PI 3.14159265358979323846
#define FOV (M_PI / 3.0)
#define MAX_DEPTH 16.0

/*Карта*/
int worldMap[MAP_HEIGHT][MAP_WIDTH] = {
    {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
    {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
    {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
    {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
    {1,0,0,0,1,1,1,0,0,1,1,1,0,0,0,1},
    {1,0,0,0,1,0,1,0,0,1,0,1,0,0,0,1},
    {1,0,0,0,1,1,1,0,0,1,1,1,0,0,0,1},
    {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
    {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
    {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
    {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
    {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
    {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
    {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
    {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
    {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}
};
//Матрица для поворотра наблюдателя вокруг
double matrix_angle[33] = {-8, -7.5, -7, -6.5, -6, -5.5, -5, -4.5, -4, -3.5, -3, -2.5, -2,
-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};

double playerX = 3.0;  /*Начальные координаты игрока*/
double playerY = 3.0;
//double playerAngle = 0.0;


void draw(double playerAngle) {
	int y, x, RayX, RayY, hit, ceiling, floor, rrandd;
	double angleDifference;
	double eyeX, eyeY, rayAngle, distanceToWall;
    printf("\033[2J"); //Очистка экрана
    printf("\033[H"); //И сброс курсора в начало
    fflush(stdout);
  

    for (y = 0; y < SCREEN_HEIGHT; y++) {
        for (x = 0; x < SCREEN_WIDTH; x++) {
			  /*Вычисляем угол для каждого луча*/
            //rayAngle = (playerAngle - FOV / 2.0) + ((double)x / (double)SCREEN_WIDTH) * FOV;
    	    rayAngle = (playerAngle - FOV / 2.0) + ((double)x / (double)SCREEN_WIDTH) * FOV;
            distanceToWall = 0.0;
             hit = 0;
             /*Можно удалить */
            //eyeX = cos(rayAngle);
            //eyeY = sin(rayAngle);
            //Тут луч начианет двигаться
            while (!hit && distanceToWall < MAX_DEPTH) {
                distanceToWall += 0.1;  /* шаг по лучу*/
                // testX = (int)(playerX + eyeX * distanceToWall); /*переменная луча по X*/
                 //testY = (int)(playerY + eyeY * distanceToWall); /*по Y*/
                RayX = (int)(playerX + cos(rayAngle) * distanceToWall); /*переменная луча по X*/
                RayY = (int)(playerY + sin(rayAngle) * distanceToWall); /*по Y*/

                if (RayX < 0 || RayX >= MAP_WIDTH || RayY < 0 || RayY >= MAP_HEIGHT) {
                    hit = 1;  /*луч вышел за границы карты, считаем, что столкнулся с границей*/
                    distanceToWall = MAX_DEPTH;
                } else {
                    if (worldMap[RayY][RayX] == 1) {
                        hit = 1; /*столкновение со стеной*/
                    }
                }
            }
			// new: Корректировка расстояния для устранения эффекта рыбьего глаза (new)
		angleDifference = rayAngle - playerAngle; // new: расчет разницы углов (new)
        if (angleDifference > M_PI) angleDifference -= 2 * M_PI; // new: приведение к диапазону (-π, π) (new)
        if (angleDifference < -M_PI) angleDifference += 2 * M_PI; // new
        distanceToWall = distanceToWall * cos(angleDifference); // new: исправление расстояния (new)


            ceiling = (int)((SCREEN_HEIGHT / 2.0) - SCREEN_HEIGHT / distanceToWall);
            floor = SCREEN_HEIGHT - ceiling;

            if (y < ceiling) {

                printf(" "); /*рисуем пробел - небо*/
            } else if (y > ceiling && y <= floor) {
                if (distanceToWall <= MAX_DEPTH / 4.0) {
                    printf("#"); /*рисуем стены разной жирности*/
                } else if (distanceToWall < MAX_DEPTH / 2.0) {
                    printf("+");
                } else {
                    printf(".");
                }
            } else {

                printf(" "); /*рисуем пол*/

            }

        }

        printf("\n");

    }
  printf("\nУгол поворта: %g\n", playerAngle);

}

int main() {
    char command[10];
    double newX,  newY;
    char c;
    int ma = 16;
    //Начальный угол игрока = 0.0
    double playerAngle = matrix_angle[16];
//Управление
    while (1) {
		
		//На каждое движение перерисовываем видимую область
        draw(playerAngle);
        printf("Введите  (W/A/S/D + enter, 'exit'  - выход): ");
        if (!fgets(command, sizeof(command), stdin)) break;

        if (strncmp(command, "exit", 4) == 0) {
            break; /*Выход из программы*/
        }

        c = command[0];
        if (c == 'w' || c == 'W') {
            /*Вперед*/
             newX = playerX + cos(playerAngle) * 0.2;
             newY = playerY + sin(playerAngle) * 0.2;
            if (worldMap[(int)newY][(int)newX] == 0) {
                playerX = newX;
                playerY = newY;
            }
        } else if (c == 's' || c == 'S') {
            /*Назад*/
            newX = playerX - cos(playerAngle) * 0.2;
            newY = playerY - sin(playerAngle) * 0.2;
            if (worldMap[(int)newY][(int)newX] == 0) {
                playerX = newX;
                playerY = newY;
            }
        } else if (c == 'a' || c == 'A') {
            /*Поворот налево*/
            ma--;
           if (ma < 0) ma = 32;
			if (ma > 32) ma = 0;
            playerAngle = matrix_angle[ma];
            //playerAngle -= 0.1;
        } else if (c == 'd' || c == 'D') {
            /*Поворот направо*/
            ma++;
			if (ma < 0) ma = 32;
			if (ma > 32) ma = 0;
             playerAngle = matrix_angle[ma];
            //playerAngle += 0.1;
        }
           
    }

    return 0;
}

AnonymUser
() автор топика
Последнее исправление: AnonymUser (всего исправлений: 2)
Ответ на: комментарий от AnonymUser
  1. Добавил в C89 матрицу вращения вручную, чтобы угол игрока не увеличивался бесконечно.
  2. Упростил формулу вычисления угла луча и прокомментировал.
  3. Убрал проверку выхода за пределы стены, она там не нужна при такой карте. https://gitflic.ru/project/dcc0/mix-c-89-php/blob?file=3dray_edited_with_matrix.c
AnonymUser
() автор топика
Последнее исправление: AnonymUser (всего исправлений: 3)

Звиздец, конечно… а ведь кто-то потом подобный код на полном серьезе вставит в бортовую электронику самолета… или уже вставил…

Vic
()
  • Markdown
Пустая строка (два раза Enter) начинает новый абзац. Знак '>' в начале абзаца выделяет абзац курсивом цитирования.
Внимание: прочитайте описание разметки Markdown.
Используйте Ctrl-Enter для размещения комментария