ДЗ Лабораторная работа №6 "Алгоритм A*"#
Критерий оценки:
- Реализовать решение для заданной картинки на python (3 балла)
- Реализовать решение для заданной картинки на C++ (5 баллов)
- Добавить решение для случайно сгенерирированного поля (+2 балл)
- Добавить решение для случайно сгенерированной карты (+3 балла) (карта отличается от поля тем, что каждой клетке присвоен некоторый вес)
В отчете должно быть указано следующие:
1. Титульный лист, где указаны ФИО преподавателя, номер задания, номер варианта
2. Формулировка задания (картинка)
3. Ссылка на github-репозиторий с работающим кодом 
4. Блок-схема алгоритма А*
5. Листинг 1. Откоментированный код метода А_star.
6. Пример расчета алгоритма А* для вашего варианта (манхэтонское расстоянеи)
7. Рисунки демонстраторы работы проекта с комментариями где была точка старта. Для карт добавить легенду.
8. Листинг 2. Откомментированный полный код вашего проекта. 
Описание Алгоритма А*#
A* — это алгоритм поиска пути, находящий кратчайший путь от начальной вершины до целевой в графе, используя эвристическую функцию для оценки стоимости.
Обозначения
- g(n) — стоимость пути от начальной вершины до вершины n.
- h(n) — эвристическая оценка оставшегося расстояния от n до цели.
- f(n) = g(n) + h(n) — полная оценка стоимости пути через n.
Псевдокод
1. Добавить начальную вершину в открытый список (open list).
2. Пока открытый список не пуст:
   - Извлечь вершину `n` с наименьшим `f(n)`.
   - Если `n` — целевая вершина, путь найден.
   - Переместить `n` в закрытый список (closed list).
   - Для каждой соседней вершины `m`:
     - Если `m` в закрытом списке, пропустить.
     - Вычислить `g(m)`, `h(m)`, `f(m)`.
     - Если `m` нет в открытом списке или найден более короткий путь к `m`, сохранить путь и добавить `m` в открытый список.
Пример#
Пусть есть следующая сетка (S — старт, G — цель, # — стена):
S . . .
# # . #
. . . G
Координаты:
- S = (0, 0)
- G = (2, 3)
Допущения:
- Стоимость перехода на соседнюю клетку (вверх, вниз, влево, вправо) = 1
- Эвристика h(n) = манхэттенское расстояние
| Шаг | Текущая | Open list | Closed list | Примечание | 
|---|---|---|---|---|
| 1 | (0,0) | [(0,1), (1,0)] | [(0,0)] | h(0,0)=5 | 
| 2 | (0,1) | [(1,0), (0,2)] | [(0,0),(0,1)] | f=1+4=5 | 
| 3 | (0,2) | [(1,2)] | [(0,0),(0,1),(0,2)] | f=2+3=5 | 
| 4 | (1,2) | [(2,2)] | ... | f=3+2=5 | 
| 5 | (2,2) | [(2,3)] | ... | f=4+1=5 | 
| 6 | (2,3) | [] | ... | f=5+0=5, цель достигнута | 
Итоговый путь (0,0) → (0,1) → (0,2) → (1,2) → (2,2) → (2,3)
Подсказки по написанию и работе кода#
Создание и работа с ячейками (Cell)#
- Каждый объект 
Cellпредставляет отдельную ячейку на сетке. - Метод 
update_neighborsобновляет список соседей, в которые можно перейти (не препятствия). - Методы 
make_start(),make_end(),make_barrier()и др. задают тип и цвет ячейки. 
Работа с сеткой#
- Сетка создаётся функцией 
make_grid(), которая возвращает список списков объектовCell. - Функция 
draw_grid(win, grid)отрисовывает ячейки и линии сетки. 
Алгоритм A*#
- Реализован в функции 
a_star_algorithm. - Используется манхэттенское расстояние: 
abs(x1 - x2) + abs(y1 - y2). - Каждая ячейка содержит:
 g— расстояние от старта;h— оценка до цели;f=g + h.
Поиск пути#
PriorityQueueиспользуется для выбора ячейки с минимальнымf.open_set_hash— вспомогательное множество для ускоренной проверки наличия в очереди.reconstruct_pathвосстанавливает путь по словарюcame_from.
Обработка событий и клавиш#
- Нажмите 
SPACE, чтобы запустить алгоритм A*. - Нажмите 
R, чтобы сгенерировать новое случайное поле. - Окно закрывается при событии 
QUIT. 
Цвета в визуализации#
| Цвет | Значение | 
|---|---|
| Белый | Пустая ячейка | 
| Чёрный | Препятствие | 
| Оранжевый | Начало | 
| Бирюзовый | Конец | 
| Зелёный | В очереди | 
| Красный | Посещена | 
| Фиолетовый | Путь | 
Некоторые фрагменты кода с комментариями#
    import pygame
    import random
    import math
    from queue import PriorityQueue
    # Инициализация Pygame
    pygame.init()
    # Константы
    WIDTH = 600
    GRID_SIZE = 30  # Размер поля NxN
    CELL_SIZE = WIDTH // GRID_SIZE
    WIN = pygame.display.set_mode((WIDTH, WIDTH))
    pygame.display.set_caption("A*")
    # Цвета
    RED = (255, 0, 0)
    GREEN = (0, 255, 0)
    BLUE = (0, 0, 255)
    YELLOW = (255, 255, 0)
    WHITE = (255, 255, 255)
    BLACK = (0, 0, 0)
    PURPLE = (128, 0, 128)
    ORANGE = (255, 165, 0)
    GREY = (128, 128, 128)
    TURQUOISE = (64, 224, 208)
    # Типы ячеек
    EMPTY = 0
    OBSTACLE = 1
    START = 2
    END = 3
    PATH = 4
    VISITED = 5
    def make_grid():
        grid = []
        for i in range(GRID_SIZE):
            grid.append([])
            for j in range(GRID_SIZE):
                cell = Cell(i, j)
                grid[i].append(cell)
        return grid
    def draw_grid(win, grid):
        for row in grid:
            for cell in row:
                cell.draw(win)
        # Рисуем сетку
        for i in range(GRID_SIZE):
            pygame.draw.line(win, GREY, (0, i * CELL_SIZE), (WIDTH, i * CELL_SIZE))
            pygame.draw.line(win, GREY, (i * CELL_SIZE, 0), (i * CELL_SIZE, WIDTH))
        pygame.display.update()
    def generate_random_grid(grid):
        # Очищаем сетку
        for row in grid:
            for cell in row:
                cell.reset()
        # Выбираем случайные начальную и конечную точки
        start_row, start_col = random.randint(0, GRID_SIZE-1), random.randint(0, GRID_SIZE-1)
        end_row, end_col = random.randint(0, GRID_SIZE-1), random.randint(0, GRID_SIZE-1)
        # Убедимся, что начальная и конечная точки разные
        while (start_row, start_col) == (end_row, end_col):
            end_row, end_col = random.randint(0, GRID_SIZE-1), random.randint(0, GRID_SIZE-1)
        start = grid[start_row][start_col]
        end = grid[end_row][end_col]
        start.make_start()
        end.make_end()
        # Добавляем случайные препятствия (20% ячеек)
        obstacle_count = int(GRID_SIZE * GRID_SIZE * 0.2)
        for _ in range(obstacle_count):
            row, col = random.randint(0, GRID_SIZE-1), random.randint(0, GRID_SIZE-1)
            cell = grid[row][col]
            if not cell.is_start() and not cell.is_end():
                cell.make_barrier()
        return start, end
    #include <SFML/Graphics.hpp>
    #include <iostream>
    #include <vector>
    #include <queue>
    #include <cmath>
    #include <algorithm>
    #include <random>
    using namespace std;
    using namespace sf;
    const int GRID_SIZE = 30; // Размер поля NxN
    const int CELL_SIZE = 25;  // Размер одной ячейки в пикселях
    const int WINDOW_SIZE = GRID_SIZE * CELL_SIZE;
    // Типы ячеек
    enum CellType {
        EMPTY,
        OBSTACLE,
        START,
        END,
        PATH,
        VISITED
    };
    // Структура для представления ячейки
    struct Cell {
        int x, y;
        CellType type;
        int f, g, h; // Для алгоритма A*
        Cell* parent;
        Cell(int x, int y) : x(x), y(y), type(EMPTY), f(0), g(0), h(0), parent(nullptr) {}
        // Перезагрузка оператора == для сравнения ячеек
        bool operator==(const Cell& other) const {
            return x == other.x && y == other.y;
        }
    };
            // Функция для вычисления эвристики (манхэттенское расстояние)
    int heuristic(const Cell& a, const Cell& b) {
        return //ваш код здесь
    }
    // Функция для проверки, находится ли ячейка в пределах сетки
    bool isValid(int x, int y) {
        return //ваш код здесь
    }
Также подсказка как отрисовать
    for (int i = 0; i < GRID_SIZE; ++i) {
        for (int j = 0; j < GRID_SIZE; ++j) {
            RectangleShape cell(Vector2f(CELL_SIZE - 1, CELL_SIZE - 1));
            cell.setPosition(i * CELL_SIZE, j * CELL_SIZE);
            switch (grid[i][j].type) {
            case EMPTY: cell.setFillColor(Color::White); break;
            case OBSTACLE: cell.setFillColor(Color::Black); break;
            case START: cell.setFillColor(Color::Green); break;
            case END: cell.setFillColor(Color::Red); break;
            case PATH: cell.setFillColor(Color::Blue); break;
            case VISITED: cell.setFillColor(Color::Cyan); break;
            }
            window.draw(cell);
        }
    }
Примеры интерфейса на Python, C++#
SFML (C++)

Pygame (Python)

Варианты#
| № варианта | Лабиринт | 
|---|---|
| 1 | ![]()  | 
| 2 | ![]()  | 
| 3 | ![]()  | 
| 4 | ![]()  | 
| 5 | ![]()  | 
| 6 | ![]()  | 
| 7 | ![]()  | 
| 8 | ![]()  | 
| 9 | ![]()  | 
| 10 | ![]()  | 
| 11 | ![]()  | 
| 12 | ![]()  | 
| 13 | ![]()  | 
| 14 | ![]()  | 
| 15 | ![]()  | 
| 16 | ![]()  | 
| 17 | ![]()  | 
| 18 | ![]()  | 
| 19 | ![]()  | 
| 20 | ![]()  | 
| 21 | ![]()  | 
| 22 | ![]()  | 
| 23 | ![]()  | 
| 24 | ![]()  | 
| 25 | ![]()  | 
| 26 | ![]()  | 
| 27 | ![]()  | 
| 28 | ![]()  | 
| 29 | ![]()  | 
| 30 | ![]()  | 





























