ИИ соперников Go Volt для разработчика
Текущий водитель — детерминированный автомат состояний с геометрическим следованием FAN маршруту. Он выдаёт команды управления, а движение, сцепление и удары рассчитывает общее нативное PC ядро. Трафик, датчики препятствий и применение оружия подключены; выбора альтернативных FAN ветвей, поиска бонусов и catch up в водителе нет.
Описание соответствует рабочему дереву main на 4 октября 2026 года. Оно объясняет фактически подключённый код порта и границу его источников. Версия без технических деталей — AI_EXPLAINED_RU.md.
Полный самостоятельный retail PC водитель в доступном архиве не восстановлен. Порт использует согласованное исключение из AI_SOURCE.md: адаптацию автомобильного AI из original/Xbox/FrontEndSrc и сверочной original/Xbox/Src. Это разрешённый Xbox донор, а не подтверждение точного retail PC AI. Автомобильная физика, CPU граница управления, конфигурации и ресурсы трасс рассматриваются отдельно по SOURCE_POLICY.md и PC_BASELINE.md.
Игровая цепочка донора достижима: GLP_GameLoop вызывает физический цикл, тот обновляет AI часы и вызывает CON_DoPlayerControl; CPU путь приходит в CAI_Process, затем в CAI_ProcessCall. Из последнего вызываются обновление данных, состояние гонки, оружие и коррекции. В FrontEndSrc ориентиры: gameloop.cpp:354, 371, 1093–1096; control.cpp:82–87; ai_car.cpp:1887–1894, 1910, 1967, 2016–2017, 2069, 2072–2078. В Src: gameloop.cpp:431, 1336–1339; control.cpp:84–86; ai_car.cpp:1923, 1932, 1950, 2007, 2057, 2109, 2112–2118.
Инициализация также существует: main.cpp задаёт AI часы, player.cpp вызывает CAI_InitCarAI, которая задаёт начальное состояние. OLD_AI_VERSION в ai_car.h не включён, отладочный переключатель gGazzasOvertake инициализирован FALSE. При этом _PC определён в сохранённых revolt.h, рядом используется XTL, а Src/sources.inc задаёт Xbox приложение. Такие PC фрагменты внутри донора не доказывают shipping цепочку полного retail PC водителя.
| Часть | Статус и граница |
|---|---|
| Интерполированная цель, форма руля, отдельные AI часы, состояния гонки и полёта | Адаптация активного разрешённого AI донора |
SetupCar, CPU знаковый квадрат руля, автомобильные силы и материалы | Удерживаемая поздняя PC граница; донорские настройки машин не подставляются |
FAN, TAZ, PAN и финальные Parameters.txt | PC данные; интерпретация и преобразование единиц выполняются портом |
| Кривизна и запас сцепления для планирования скорости | Собственный защитный планировщик порта |
| Ограниченная величина коррекции руля при заносе, лучи, поиск проекции и перестановка сценой | Адаптации порта |
| Момент использования оружия | Политика Xbox донора с перечисленными ниже изменениями |
Аудит audit_late_pc_ai.py закрепляет 14 копий выбранных функций и PC границу входа. Различия этих копий: в Src есть clamp руля только для _N64 и явные преобразования в char; FrontEndSrc полагается на присваивание. Основные выбранные функции гонки, полёта и часов совпадают. Аудит не покрывает весь AI, все оружейные функции или все константы адаптера и не заменяет проверку поведения.
| Файл | Функции и строки | Ответственность |
|---|---|---|
| race_scene.gd | _spawn_opponents 351, _step_vehicles_shared 588, _update_opponents 512 | Создание машин, общий шаг, передача наблюдений и команд |
| pc_ai_driver.gd | configure 114, will_decide 230, advance_physics 236, advance 253 | Планировщик решений, маршрут и автомат состояний |
| pc_ai_sensors.gd | observe 6, handling_limits 38, _clearance 58 | Соседи, боковое скольжение, живой запас сцепления и лучи |
| legacy_race_route.gd | load_track 23, parse_fan_bytes 118, _main_route 223 | Чтение PC маршрутных файлов и основного кольца |
| pc_weapon_ai.gd | should_fire 38 | Условия применения уже полученного предмета |
| pc_pickups.gd | consider_ai 364, before_substep 225, after_substep 295 | Общий инвентарь, сбор, запрос огня и жизненный цикл |
| pc_weapons.gd | target_for 367 | Общий выбор ракетной цели и исполнение оружия |
В Single Race _advance_race_step обновляет сессии и AI перед подготовкой нативного подшага. Затем сцена подготавливает управление, обновляет объекты и оружие, решает взаимные контакты машин и выполняет физику. Все машины получают общий план подшагов. Обычные самостоятельные _physics_process машин выключены при создании гонки.
В _spawn_opponents создаются три автомобиля из трёх следующих записей каталога после выбранной игроком машины, циклически. Для каждого создаются отдельные водитель и гоночная сессия. Начальный максимум передаётся как config.top_speed * 0.44704. До старта и после собственного финиша соперник получает нулевой газ и включённый тормоз. В Time Trial соперники не создаются.
advance_physics(position, forward, velocity, grounded_wheels, delta, body_contacts, upright_dot, observation) принимает мировые Godot векторы, метры, метры в секунду и секунды. grounded_wheels находится в диапазоне 0..4; upright_dot — скалярное произведение оси вверх автомобиля с мировой вертикалью. Скалярные наблюдения и геометрия проверяются на конечность; невалидное решение останавливает машину и переводит сцену в обработку ошибки.
Основной выход содержит throttle, steer, brake, reverse, recovery, state, target, target_speed, route_index, route_distance, lookahead, wrong_way и диагностические признаки. throttle и steer нормированы к диапазону −1..1. Фактически сцена передаёт в автомобиль только throttle, steer, brake. Поле reverse описывает режим; заднее движение запрашивается отрицательным газом. Поле recovery обрабатывает сама сцена.
brake=true в этом автомобильном API означает ручник с блокировкой задних колёс. Для обычного замедления водитель оставляет его false и использует отрицательный запрос двигателя.
Пространство PC переводится в Godot масштабом 0.005, то есть 200 исходных единиц на метр, с преобразованием осей (x, y, z) → (x, −y, −z). Пределы FAN в mph переводятся множителем 0.44704. RacingLineSpeed сохраняется как исходный рейтинг 0..100; текущий водитель не превращает его в mph. Точное исходное преобразование этого рейтинга в скорость не восстановлено.
Физика выполняется с подшагом 8 мс. Новый AI output рассчитывается с интервалом DECISION_INTERVAL = 0.033 с; внутренние таймеры решения увеличиваются на DECISION_DELTA = 1/30 с. Это сохраняет различие донора между целым 1000/30 = 33 мс и временем ONE/30.
will_decide — чистый запрос расписания. Сцена вызывает датчики только когда предстоит решение. advance_physics уменьшает остаток времени, возвращает прежний словарь целиком на промежуточных шагах и вызывает advance при наступлении срока. После задержки не создаётся очередь решений по устаревшему снимку.
Следовательно, таймеры состояния, цель, обнаружение препятствия и изменение эффективного максимума скорости видят мир только на новом решении. Комментарий внутри advance о чтении значения «каждый кадр» не отменяет удержания output. При 8 мс решения попадают на сетку подшагов; это не ровно один вызов каждые четыре шага. В проверке 20 секунд получается 607 решений на 2500 физических шагов.
Оружейный consider_ai вызывается сценой на подшаге с последним выходом водителя и текущим оружейным статусом. Его частота отдельно не ограничена расписанием водительских решений.
load_track объединяет FAN навигацию, TAZ контрольные зоны, PAN прогресс, INF старт и TRI триггеры. Водитель получает массив main_route_points и соответствующие FAN узлы. FAN основной цикл строится по Next[0]; другие связи читаются как данные, но водитель не выбирает их во время гонки.
Для каждого узла есть две границы и доли racing_line, overtaking_line. Точки линий строятся как boundary[1].lerp(boundary[0], fraction). Во время движения сначала смешиваются гоночная и обгонная точки по _line_blend, затем интерполируется цель внутри отрезка.
Привязка использует горизонтальную проекцию на сегмент с ограничением доли к 0..1. Оценка кандидата равна квадрату расстояния плюс 0.01 * (1 − forward.dot(segment_direction)). Добавка только разрывает почти равные варианты у пересечения. Обычный поиск охватывает 4 сегмента позади, текущий и 18 впереди. Если найденный путь дальше 8 м, выполняется полный поиск кольца.
Внутренняя route_distance водителя измеряется по горизонтальным длинам базовых сегментов. Её нельзя без преобразования смешивать с длиной loader в 3D, сохранёнными дистанциями FAN или PAN дистанцией до финиша.
Обычная дальность цели задаётся выражением lookahead = 3 + 0.1 * horizontal_speed в метрах. При коррекции используется цель на 1 м впереди. Датчики получают цели через get_sensor_targets до текущего advance, поэтому их геометрия использует последнее сохранённое положение по маршруту и предыдущую дальность обзора.
Для горизонтального направления на цель вычисляется знаковый угол e = atan2(desired.dot(right), desired.dot(forward)). Коэффициент живой машины из handling_limits равен G = 45 / abs(degrees(FL.SteerRatio)), то есть половине 90/fullLockAngle из SetupCar и активной CAI_ProcessCarSteering.
Обычная команда имеет вид:
u = sign(e) * min(1, sqrt(max(0, 1 - abs(cos(e)))) * G)
steer = clamp(round(16 * u) / 16, -1, 1)
Порт использует симметричную квантизацию 1/16. Это адаптация байтовой квантизации донора, не побайтная сборка исходного контроллера. Затем поздняя PC CPU граница применяет знаковый квадрат steer * abs(steer) и штатное изменение угла колёс. Маршрутный вход проходит этот квадрат один раз.
В маршрутных fixtures без автомобильных наблюдений существуют значения по умолчанию STEER_CONVERSION = 1.15, LATERAL_ACCELERATION = 4.5. Живая сцена передаёт параметры конкретной машины; эти значения по умолчанию не являются общей настройкой всех соперников.
Обычный предел берётся прежде всего из observation.physics_top_speed_mps. Сцена передаёт текущий нативный get_report().top_speed_mps и в этот ключ, и в weapon_top_speed_mps. Поэтому учитываются реальные изменения режима машины и активных эффектов. Без первого ключа используется максимум начального TopSpeed и положительного оружейного значения.
Явные поддержанные FAN приоритеты сохраняют предел min(initial_top_speed, mph * 0.44704):
| Priority | Предел mph | Предел м/с до ограничения машиной |
|---|---|---|
| 4 | 25 | 11.1760 |
| 17 | 15 | 6.7056 |
| 18 | 20 | 8.9408 |
| 19 | 30 | 13.4112 |
_speed_cap_ahead выбирает минимум поддержанных ограничений в пределах 12 м по маршруту. Остальные типы Priority не становятся новыми ограничениями автоматически.
Планировщик кривизны — адаптация порта: вход в отдельное состояние тормозной зоны у донора отключён if (0). Его нельзя описывать как перенесённую рабочую тормозную зону оригинала.
handling_limits вычисляет бюджет ускорения по слабейшему из четырёх текущих колёс:
oil_i = 0.5 * (oil_time_i + 0.2) / 2.7, если oil_time_i < 2.5
oil_i = 1, иначе
a = max(0.001, min_i(kinetic_friction_i * roughness_i * oil_i) * gravity * 0.005)
Единица a — м/с². Это консервативная оценка при статической гравитационной нагрузке; аэродинамический прижим не добавляется. Например, у травы roughness равен 0.7; другое поле материала Gripiness нельзя подставлять вместо него.
Кривизна оценивается по двум соседним горизонтальным хордам длиной примерно 2 м. Для смещений d = 0, 2, 4, 6, 8 м берутся точки s+d, s+d+2, s+d+4, то есть горизонт достигает 12 м. k = angle / max(0.5, mean_chord_length), где угол в радианах, k в 1/м. Ограничение для каждого изгиба:
v_limit = sqrt(0.5 * a / k + a * d)
target = min(route_cap, все v_limit)
heading_scale = clamp(1.15 - 0.45 * abs(e), 0.35, 1)
target_speed = clamp(target * heading_scale, min(3, target), target)
Половина бюджета оставлена для поворота, половина — для замедления. Кривизна считается по гоночной линии, а не по текущей смешанной линии обгона. При большом боковом удалении, оставшемся больше 8 м даже после полного поиска, цель скорости дополнительно ограничивается 5 м/с.
Если выбранный датчик вернул конечную дистанцию c, добавляется предел max(0, (c − 0.25) / 0.35) м/с. Он может снизить цель до нуля.
При speed > target_speed + 0.1 и скорости вперёд больше 0.5 м/с команда двигателя равна −clamp((speed − target_speed) / max(1, 0.25 * target_speed), 0.2, 1). Если скорость вперёд мала, подаётся ноль. В остальных случаях подаётся полный газ 1; дальнейшая коррекция заноса может уменьшить положительный запрос. Сам TopSpeed — параметр возможностей двигателя, а не гарантия достижения ровно этой скорости.
_clearance выполняет по три луча к каждой из двух авторских линий. Лучи идут по центру и со смещением на половину колеи; начало поднято на 0.04 м от позиции автомобиля. Длина — clamp(length(linear_velocity) * 0.35 + 0.7, 0.7, 5) м. Здесь используется полная пространственная скорость, в отличие от горизонтальной скорости основной цели.
Препятствием считается попадание с abs(normal.dot(UP)) < 0.5. Пологая поверхность не включается в эту оценку. Возвращается минимальная дистанция подходящего попадания или INF при его отсутствии.
Лучевая маска трассы — слой 1; автомобили находятся на слое 2. Поэтому эти лучи проверяют окружение, а не прогнозируют столкновения с автомобилями. Соседи передаются отдельно через peers.
Исходное ProcessNearWallAvoidance предсказывало положение машины и сравнивало его с плоскостями границ FAN узлов; порт заменил этот механизм шестью лучами геометрии. Адреса донора: FrontEndSrc/ai_car.cpp:2420, Src/ai_car.cpp:2460.
_choose_avoidance_line берёт предпочитаемую обгонной логикой линию. Если она перекрыта, а другая свободна либо имеет запас больше на 0.25 м, выбирается другая крайняя линия. Иначе сохраняется плавная смесь. Для ограничения скорости clearance выбирается дискретно: обгонная линия при смеси не меньше 0.5, гоночная иначе. Swept volume и проверка всей промежуточной смешанной траектории не выполняются.
_update_traffic выбирает ближайшего подходящего лидера по следующим условиям:
- Горизонтальное прямое расстояние больше 0.001 м и не больше 4 м; смещение направлено вперёд относительно автомобиля.
- При наличии зон лидер находится в той же TAZ зоне или следующей с циклическим переходом.
- Положительный разрыв по маршруту больше 0.001 м и меньше 2.5 м.
Если переданы круги обеих машин, контракт умеет считать абсолютный разрыв по кругам. Текущий _observe_driver передаёт зоны и состояние обгона, но не lap и не peer.route_distance. В живой гонке используется локальная проекция соседа и циклическая разность дистанций. Скорость соседа передаётся и валидируется, но не участвует в выборе лидера или оценке сближения.
Если лидер обгоняет, destination ставится в 0, иначе в 1. Для AI признак поступает из последнего output; для игрока он оценивается по близости к двум линиям. Таймер снова ставится в 4 с при каждом обнаружении лидера; без лидера уменьшается, после истечения destination возвращается в 0.
line_blend меняется через move_toward со скоростью 0.1 в секунду внутреннего времени решений. Полный переход 0→1 занял бы около 10 с. Флаг overtaking означает намерение или ненулевую смесь, а не факт завершённого обгона. Отдельной проверки бокового свободного коридора, относительной скорости или стратегии нескольких обгонов нет.
observe берёт точки левого переднего и левого заднего колеса, индексы 0 и 2. Скорость каждой точки равна linear_velocity + angular_velocity.cross(world_offset). Боковая составляющая измеряется вдоль направления поперёк шины с учётом текущего поворота колеса. Это измерения двух точек, не усреднение всех колёс оси.
Коррекция допускается при минимум трёх опирающихся колёсах и горизонтальной скорости не меньше 2 м/с. Для боковых скоростей f и r вычисляется:
excess = abs(r) - abs(f) - 0.35, если sign(f) == sign(r)
excess = abs(f - r) - 0.35, иначе
reference = f, если abs(f) > 0.05, иначе r
c = sign(reference) * min(0.3, excess / max(speed, 2))
wheel_target = clamp(current_normalized_wheel_angle + c, -1, 1)
steer = sign(wheel_target) * sqrt(abs(wheel_target))
При неположительном excess или слишком малом reference поправки нет. Ограничение 0.3 относится к пространству нормализованного угла колёс. Обратный знаковый корень нужен, чтобы последующая PC квадратичная граница не уменьшила поправку повторно.
Положительный газ умножается на 1 − min(0.65, 1.5 * abs(c)). При текущем ограничении abs(c) ≤ 0.3 максимальное снижение составляет 45 процентов. После этой коррекции финальная команда руля уже не обязана быть кратной 1/16. Величины коррекции — адаптация порта, без скрытого переноса Xbox таблиц управляемости.
Сначала обновляются таймеры проблем и проверяется необходимость reposition; затем состояния исправления направления и полёта; обычные скорость и руль вычисляются, если ранний выход не произошёл.
| Условие | Порог | Действие |
|---|---|---|
forward.dot(route_tangent) < −0.15 | 0.35 с | Из race в reverse_correct |
| Контакт кузова и скорость меньше 3 mph | 0.25 с | Из race в reverse_correct |
| Нет ни одного опирающегося колеса | В race | Перейти в airborne; газ 1, руль 0 |
| Возобновился контакт колеса | В airborne | Вернуться в race |
| Исправление задним ходом | 0.9 с | Газ −0.7 и руль против сохранённого знака; затем forward_correct |
| Исправление вперёд | До 1.4 с или dot больше 0.55 | Газ 0.65 и руль по сохранённому знаку; затем race |
| Не вышел из области вокруг anchor на 0.75 м | 2.5 с | Запрос recovery |
| Боковая дистанция до маршрута больше 12 м | 1 с | Запрос recovery |
upright_dot < 0.25 | 1.2 с | Запрос recovery |
Таймеры выше — внутреннее время решений по 1/30 с. Знак коррекции хранится и обновляется на каждом решении по ошибке направления или стороне относительно маршрута, в том числе во время исправления задним ходом и вперёд. Порог anchor измеряет физическое горизонтальное перемещение, поэтому вращение колёс на месте не считается прогрессом.
Сцена дополнительно восстанавливает машину ниже нижней границы трассы на 15 м. _recover_opponent не начисляет новый прогресс: выбирает район последней принятой TAZ зоны, ресинхронизирует сессию и бонусы, сбрасывает водительские таймеры и интерполяцию.
_vacant_recovery_pose пробует 13 позиций: исходную и смещения назад по 0.75 м с повторной привязкой к маршруту. Требуется поверхность с normal.dot(UP) ≥ 0.5 и дистанция до других машин больше суммы радиусов. Радиус определяется по оболочке кузова с запасом 0.05 м. Если всё занято, перестановка откладывается.
consider_ai использует общий слот инвентаря и общую weapons.target_for. Статус эффектов сливается с контекстом TAZ зоны, ширины FAN и wall flags. При положительном should_fire вызывается общий request_fire; действительный выстрел выполняется системой бонусов. После успешного AI применения действует отдельный cooldown 0.35 с.
Общие проверки: предмет есть; он удерживается минимум 0.4 с; состояние race; нет recovery и wrong_way; минимум три колеса опираются; upright_dot ≥ 0.7; power_remaining ≤ 0. Для поиска ближайшей машины и допустимой цели используется та же или соседняя TAZ зона, циклически в обе стороны. Дистанции оружейной политики пространственные, не расстояния по маршруту.
| Предмет | Условие применения после общих проверок |
|---|---|
| Shockwave | Ближайшая машина в передней полуплоскости ближе 20 м |
| Firework и Firework Pack | Общая выбранная цель находится впереди; дистанция строго между 1.5 и 20 м |
| Water Bomb | Ближайшая передняя машина строго между 1 и 20 м |
| Electro Pulse | Машина впереди ближе 2.5 м или позади ближе 3 м; электропульс ещё не активен |
| Oil | При собственной скорости больше 1 м/с машина позади в коридоре до 6 м, с боковым смещением меньше 1 м и вертикальным меньше 0.5 м; либо условие узкой дороги |
| Chrome Ball и Clone | Машина позади ближе 3 м; либо условие узкой дороги |
| Turbo | Скорость больше 1 м/с, газ больше 0.6, abs(steer) < 0.12, dot направления на цель не меньше 0.9 |
| Global | Применить при выполнении общих проверок |
| Putty Bomb | Не нажимать повторно; общая PC выдача активирует бомбу при получении |
Условие узкой дороги: скорость строго больше 10 mph, ширина не больше 4 м, а внутри этого диапазона либо ширина не больше 2 м, либо есть левый или правый wall flag.
Основной донорский обработчик — CAI_ProcessWeapons, FrontEndSrc/ai_car.cpp:3054 и Src/ai_car.cpp:3097; просмотренные тела совпадают. Внутри донора 200 единиц соответствуют метру. Активные расстояния 20, 1.5, 1, 2.5 и 3 м и выравнивание Turbo 0.9 объясняют основу политики. Её явные отличия:
- Проверки удержания, опоры, вертикальности и cooldown добавлены портом.
- Коридор масла расширен до 6 м; условие близкой задней машины соединяется с условием узкой дороги через OR, тогда как у донора есть
else. - Ракета проверяет расстояние до реально выбранной цели; донор проверяет наличие цели вместе с дистанцией ближайшего переднего автомобиля.
- Донор блокирует атаки на
PLAYER_LOCALприPlayers[0].car.LastHitTimer ≤ 10 с; это исключение для игрока в текущую политику не перенесено. - Индексы и снимки состояния заменяют донорские указатели; исполнение и эффекты остаются в общей PC системе порта.
Ракетная цель выбирается общим target_for: вертикальная компонента смещения умножается на 5, затем действуют направленный диапазон и score, повышающий вес подходящего направления. Это отдельная операция от выбора момента огня. Сбор предметов общий для всех машин, но вероятности выдачи не одинаковы: текущий pc_pickup_state.get_weight_table использует для CPU фиксированную PC строку, для игрока — интерполяцию по месту в гонке.
Водитель не обучается, не использует нейросеть или A star, не назначает индивидуальные характеры соперникам. Различия движения возникают из настроек машин и наблюдений. Он не меняет физические силы ради удержания темпа игрока и не реализует catch up. Настоящие эффекты предметов могут менять возможности машины через общую симуляцию.
Он не выбирает Next[1], не прокладывает путь к pickup и не использует RacingLineSpeed как восстановленную скорость. Обгон реагирует на близость, без проверки относительной скорости и бокового коридора. Датчики окружения не покрывают всю траекторию кузова. Автомобиль может сорваться с маршрута после удара; восстановление остаётся отдельной адаптацией сцены.
Детерминированность относится к решению водителя при одинаковых входах и состоянии. Воспроизводимость полного заезда дополнительно зависит от шага физики, порядка взаимодействий и seed системы бонусов; побитная идентичность на любом оборудовании не утверждается.
Исторические разделы AI_SOURCE.md про первые инкременты содержат фразу об ещё не реализованном оружии. К текущему порту она не относится: цепочка consider_ai → should_fire → request_fire подключена. Старый отчёт не должен переопределять текущую реализацию.
4 октября 2026 года существующие проверки были повторно выполнены без изменения игровой логики:
| Проверка | Что она проверяет | Результат |
|---|---|---|
audit_late_pc_ai.py --check | 14 выбранных копий функций, часы и PC CPU граница | Код возврата 0 |
ai_driver_smoke.gd | Маршрут, команды и состояния водителя | 0 failures |
ai_traffic_smoke.gd | Обгон, наблюдения и препятствия | 0 failures |
weapon_ai_smoke.gd | Условия применения предметов и границы | 0 failures |
ai_weapon_speed_smoke.gd | Активный максимум скорости и маршрутные ограничения | 0 failures |
ai_late_physics_smoke.gd | Формулы, PC машины и реальные поездки по nhood1 | 42 703 checks, 0 failures |
Последний сценарий использует доступные нативные RVRuntime, RVVehicleSimulation и подготовленные PC ресурсы. На прямой игрок с полным газом и водитель AI дают совпадающие скорости и позиции в пределах проверки для RC Bandit, Dust Mite, Phat Slug и Col. Moss. За 20 секунд каждый водитель делает 607 решений на 2500 физических шагов. В отдельных поездках по nhood1 все четыре обходятся без восстановления и режимов коррекции. Полный многомашинный трёхкруговой заезд в рамках подготовки этих документов не повторялся.
Пример запуска из корня проекта:
& 'C:\Users\gboyd\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\python.exe' 'tools/audit_late_pc_ai.py' --check
& '.\.tools\godot\Godot_v4.7.2-stable_win64_console.exe' --headless --path '.\port\godot' --script 'res://tests/ai_late_physics_smoke.gd'
Остальные сценарии запускаются заменой имени после res://tests/. Результаты этого прогона сохранены в .build/ai-documentation-checks/. Тесты показывают соответствие перечисленным сценариям, а не полноту восстановления retail PC AI.
- pc_ai_driver.gd: 230–250 расписание, 253–395 решение и состояния, 455–500 трафик, 503–516 выбор линии, 519–546 занос, 549–570 проекция, 577–588 выбор точки, 591–631 планировщик скорости, 638–645 обычный предел, 648–653 маршрутный руль.
- pc_ai_sensors.gd: 6–35 наблюдения, 38–55 управление и сцепление, 58–82 лучи.
- race_scene.gd: 351–400 состав соперников, 512–579 подключение решений и наблюдений, 588–687 общий шаг, 861–892 восстановление, 941–971 поиск свободного места.
- pc_weapon_ai.gd: 38–107 политика, 125–230 близость, цель, коридор, ширина и зоны.
- FrontEndSrc ai_car.cpp и Src ai_car.cpp: разрешённый AI донор; адреса активной цепочки и оружия указаны выше.
- FIX_18_7_AI.md, audit_late_pc_ai.py и late_pc_ai_manifest.json: история адаптации и точная граница текущего аудита.