В настоящее время существует много мануалов как правильно делать запросы, в основном они касаются привычного всем языка, причём, неважно какой он, модель преобразует его в вектор который одинаков для большинства людей. Нюансы вроде системы времён, падежей, фиксированной или гибкой последовательности слов мало влияют на структурированный вывод программ, в основном они нужны лишь для художественной генерации или передачи смысла текста от человека к человеку.
Задача довольно простая — как с использованием минимально возможного набора слов без приставок, суффиксов, окончаний или с их минимумом, а также союзов и других вспомогательных речевых конструкций получить на выходе годный код с 0% ошибок и 90% функционала согласно такому промпт-ТЗ.
Итак, приступим. Будем использовать VS Code/блокнот с пустой папкой.
Для начала необходимо развернуть виртуальное окружение. Для этого пишем следующий промпт. >Замечание — пора на Хабре добавить в язык программирования «Промптинг»<
Развёртывание виртуального окружения
Хотеть бэш скрипт проверь .venv если нет создать, обновить pip последняя версия найти requirements.txt установить библиотеки файл этот. Опция аргумент имя файла.py скрипт проверь наличие окружения .вэнв если нет установи рекварементс.txt. Если .вэнв уже есть запускай питон 3 скрипт проверь что запуск внутри вэнв. Хотеть список зависимостей флаг —g сохранить в файл requirements-текущая дата-время.txt изменить версия == на >=. выйди из вэнв по завершению питона.
Данный промпт генерирует следующий код
#!/bin/bash# Имя виртуального окруженияVENV_DIR=".venv"# Имя файла скрипта из аргументаPY_SCRIPT=$1# 1. Если передан флаг --g, сохраняем зависимости и выходимif [[ "$1" == "--g" ]]; then if [ -d "$VENV_DIR" ]; then source "$VENV_DIR/bin/activate" FILENAME="requirements-$(date +'%Y%m%d-%H%M%S').txt" # Сохраняем, меняя == на >= pip freeze | sed 's/==/>=/g' > "$FILENAME" echo "Зависимости сохранены в $FILENAME" deactivate else echo "Ошибка: .venv не найден. Нечего сохранять." fi exit 0fi# 2. Проверка и создание .venvif [ ! -d "$VENV_DIR" ]; then echo "Создаю виртуальное окружение..." python3 -m venv "$VENV_DIR" source "$VENV_DIR/bin/activate" echo "Обновляю pip..." pip install --upgrade pip if [ -f "requirements.txt" ]; then echo "Устанавливаю библиотеки из requirements.txt..." pip install -r requirements.txt fielse source "$VENV_DIR/bin/activate"fi# 3. Запуск Python скрипта (если передан аргумент)if [ -n "$PY_SCRIPT" ]; then if [ -f "$PY_SCRIPT" ]; then echo "Запуск $PY_SCRIPT внутри $(which python)..." python3 "$PY_SCRIPT" else echo "Ошибка: Файл $PY_SCRIPT не найден." fielse echo "Окружение готово. Имя скрипта для запуска не указано."fi# 4. Выход из venvdeactivate
запуская командой ./generate_requirements.sh my.py можно получить запуск скрипта с автоматической установкой виртуального окружения на основе файла requirements.txt когда виртуальное окружение уже создано и имеется повторный запуск применяется уже к установленному venv. Пример файла requirements.txt
pygame
PyOpenGL
PyOpenGL_accelerate
numpy
matplotlib
Если необходимо сканировать пакеты которые установлены, запускается скрипт с флагом —g, создаётся файл с таймстемпом requirements-20260427-213724.txt
содержащий
contourpy>=1.3.3
cycler>=0.12.1
fonttools>=4.62.1
kiwisolver>=1.5.0
matplotlib>=3.10.9
numpy>=2.4.4
…
подправленные версии с точки зрения последующей установкой новых
my.py — любой hello world, но лучше использовать промпт
пример Питон numpy вектор вывод умножение число
import numpy as np# Создание вектораvector = np.array([1, 2, 3])# Умножение на числоresult = vector * 5print(result)# Вывод: [ 5 10 15]
Обратить внимание на характер фраз и введение «промпт» переменных такой как вэнв и скрипт.
Следующий пример — создаём цифровой фильтр с БИХ характеристикой на языке С и проверяем его с генерацией выходного csv файла с отсчётами и значениями коэффициентов.
Цифровой фильтр — ядро
хотеть цифровой фильтр чистый С файл калькюляйт си функция калькуляйт цикл по n отсчётам вход n, массив выходов размер n, массив отсчётов х, и коэффициентов сх одинаковые размер, число коэффициентов эникс, массив отсчётов у, и коэффициентов су одинаковые размер, число коэффициентов энигрек, отсчёты и коэффициенты флоат остальное инт размер. внутри аккумулятор акк ноль, вход х нулевой константа задать 1 затем цикл умножение с накоплением сх и с цикл умножение с накоплением су и у в аккумулятор. сдвигать элементы массивы х и у прямой порядок отсчётов на один только инкремент индекса плюс плюс, начать индекс ноль использовать временная переменная t1 t2 обмен сохранить аккумулятор массив выходов
По данному промпту генерируется следующий код, причём, +- одинаковый для любых моделей.
void calculate(int n, float* outputs, float* x, float* cx, int nx, float* y, float* cy, int ny) { for (int i = 0; i < n; i++) { float acc = 0.0f; // Входной отсчет x[0] константа 1 (согласно запросу) x[0] = 1.0f; // Цикл умножения с накоплением для входов (сх и х) for (int j = 0; j < nx; j++) { acc += cx[j] * x[j]; } // Цикл умножения с накоплением для выходов (су и у) // Обычно у[0] не используется в сумме для расчета текущего у[0], // поэтому начинаем с 1, если су[0] не предусмотрен как обратная связь for (int j = 1; j < ny; j++) { acc += cy[j] * y[j]; } // Сохраняем результат outputs[i] = acc; y[0] = acc; // Сдвиг элементов массивов х и у (прямой порядок, инкремент ++) // Используем временные переменные t1, t2 для обмена (сдвига) float t1, t2; // Сдвиг X t1 = x[0]; for (int j = 1; j < nx; j++) { t2 = x[j]; x[j] = t1; t1 = t2; } // Сдвиг Y t1 = y[0]; for (int j = 1; j < ny; j++) { t2 = y[j]; y[j] = t1; t1 = t2; } }}
Отличный фильтр! Как и требуется — умножение с накоплением, сдвиг элементов на 1 только с использованием инкремента указателя. На архитектурах DSP процессоров есть команды, которые используют пост-инкремент указателя. Пример кода ассемблера и промпт для этого:
хотеть реализация сдвиг массивов Х и У фрагмент кода ассемблер для Техас Инструменты процессор семейства С2000 используй пост-инкремент
; Предположим:; XAR4 указывает на начало массива (X[0]); XAR5 указывает на X[1]; AL используется как временная переменная (T1); PH используется как счетчик (NX - 1) MOVZ AR6, @NX ; Загружаем NX SUBB XAR6, #1 ; Нам нужно NX-1 итераций для сдвига MOVZ AR0, @AR6 ; Переносим в AR0 для использования в BANZ ; Настройка указателей MOVL XAR4, #_X ; Адрес X[0] MOVL XAR5, #_X ; Адрес X[0] ADDB XAR5, #1 ; Теперь XAR5 указывает на X[1]SHIFT_LOOP: MOV AL, *XAR4 ; T1 = X[i] MOV AH, *XAR5 ; Временный буфер для обмена (T2) MOV *XAR4++, AH ; X[i] = X[i+1], инкремент указателя i MOV *XAR5++, AL ; X[i+1] = T1, инкремент указателя i+1 BANZ SHIFT_LOOP, AR0-- ; Цикл, пока AR0 не станет 0
Довольно неплохо, но требует проверки, которую оставим за кадром. Что касается оптимизации то можно получить классическое решение
хотеть реализация ассемблер код фрагмент фильтр для Техас Инструменты процессор семейства С2000 используй рэп и мак
; На входе:; XAR4 - указатель на массив данных (X); XAR5 - указатель на массив коэффициентов (CX); PH - количество коэффициентов (NX); ACC - результат (аккумулятор) ZAPA ; Обнулить ACC и P (регистр произведения) MOVZ AR0, @NX ; Загрузить количество итераций (NX) SUBB XAR0, #1 ; RPT выполняет N+1 раз, поэтому вычитаем 1 ; --- Цикл умножения с накоплением (MAC) --- ; RPT повторяет следующую инструкцию AR0 раз ; *XAR4++ : берет X[i], пост-инкремент адреса ; *XAR5++ : берет CX[i], пост-инкремент адреса RPT @AR0 MAC *XAR4++, *XAR5++, ACC ADDL ACC, P ; Добавить последний результат произведения в ACC
Достаточно попросить использовать команды RPT (рэп) — повторение заданного количества раз в регистре и MAC (мак) — умножение с накоплением. Последовательность на ассемблере RPT-MAC означает выполнить эту команду заданное количество раз, очень удобно для повторяющихся операций.
Для оптимизации попросить MACD. Оптимизированная версия
хотеть реализация ассемблер код фрагмент фильтр для Техас Инструменты процессор семейства С2000 используй команду Макдональдс вместо Т1 Т2
; XAR4 — указывает на ПОСЛЕДНИЙ элемент массива данных X (X[NX-1]); XAR5 — указывает на ПОСЛЕДНИЙ элемент массива коэффициентов CX (CX[NX-1]); ACC — аккумулятор (результат); Обязательно: массивы должны идти "задом наперед" для логики сдвига MACD SETC SXM ; Включить знаковое расширение ZAPA ; Обнулить ACC и P MOVZ AR0, @NX ; Загружаем число коэффициентов SUBB XAR0, #1 ; Корректировка для RPT (выполняет N+1 раз) ; --- Главный цикл фильтрации --- ; MACD делает: P = *XAR4 * *XAR5, ACC = ACC + P, ; затем копирует: *(XAR4+1) = *XAR4 (сдвиг!) ; и делает декремент: XAR4--, XAR5-- (пост-декремент) RPT @AR0 MACD ACC, *XAR4--, *XAR5-- ADDL ACC, P ; Добавить финальное произведение в ACC
Получаем полностью оптимизированный вариант. Но это уже другая история, какой командой обнулить аккумулятор.
Создаём заголовочник
хотеть калькюляйт аш с прототипом функции кальк, документ и кислород но будь осторожен с типами и указателями
/** * @file calculate.h * @brief Реализация цифрового фильтра (Прямая форма I). */#ifndef CALCULATE_H#define CALCULATE_H/** * @brief Вычисляет выходные значения цифрового фильтра. * * Функция проходит циклом по n отсчетам, выполняя умножение с накоплением (MAC) * и обновляя линии задержки x и y. * * @param n [in] Количество итераций (отсчетов). * @param outputs [out] Массив для записи результатов (размер n). * @param x [in,out] Массив предыдущих входных отсчетов (размер nx). * @param cx [in] Массив коэффициентов для входов (размер nx). * @param nx [in] Количество входных коэффициентов. * @param y [in,out] Массив предыдущих выходных отсчетов (размер ny). * @param cy [in] Массив коэффициентов обратной связи (размер ny). * @param ny [in] Количество выходных коэффициентов. */void calculate(int n, float* outputs, float* x, float* cx, int nx, float* y, float* cy, int ny);#endif // CALCULATE_H
Любезно позаботился сделав Doxygen формат описания, необходимый как воздух (кислород)
Далее продолжаем с новым контекстным окном. Пробуем сопряжение без запоминания предыдущего, описываем прототип функции промптом вместо кода. Рассчитываем коэффициенты цифрового фильтра — нерекуррентная часть скользящее среднее, рекуррентная — комплексно сопряжённые (колебательные) полюса, расположенные случайным образом на комплексной окружности.
Генерация коэффициентов цифрового фильтра и главная функция
Хотеть проверку чистый С вызови готовая функция калькюляйт прототип аргументы вход N, массив выходов вых размер N, массив отсчётов Х, и коэффициентов СХ одинаковые размер, число коэффициентов ЭнИкс, массив отсчётов У, и коэффициентов СУ одинаковые размер, число коэффициентов ЭнИгрек, отсчёты коэффициенты флоат остальное инт, инклуд калькюляйт аш без заглушек. Сделать формирование коэффициентов СХ и СУ так что СУ индекс ноль всегда ноль. Коэффициенты СХ одинаково единица делённая на ЭнИкс размер. Коэффициенты СУ функция рандом внутри единичной комплексная окружность устойчивость учти, альфа амплитуда количество по размеру ЭнИгрек. Хардкод Пи. Обнули массивы Х и У. Массивы статические макросы ЭнИкс 5 ЭнИгрек 7 Эн 50. Дай вывод CSV файл и на экран сначала коэффициенты СХ СУ потом отсчёты вых Эн с индекс. Внимательно вызов калькюляйт число аргументов типы и массивы последовательность.
Вывод
#include <stdio.h>#include <stdlib.h>#include <math.h>#include <time.h>#include "calculate.h"#define NX 5#define NY 7#define N 50#define PI 3.14159265358979323846int main() { // Статические массивы float out[N]; float x[NX] = {0}; float cx[NX]; float y[NY] = {0}; float cy[NY]; srand((unsigned int)time(NULL)); // 1. Формирование CX: 1/NX for (int i = 0; i < NX; i++) { cx[i] = 1.0f / NX; } // 2. Формирование CY: устойчивые коэффициенты // CY[0] всегда 0 по условию cy[0] = 0.0f; // Генерируем остальные как малые случайные величины для устойчивости // (Сумма модулей CY < 1 — достаточное условие устойчивости) for (int i = 1; i < NY; i++) { float alpha = (float)rand() / RAND_MAX; // амплитуда < 1 float phase = (float)rand() / RAND_MAX * 2.0f * PI; // Берем проекцию на вещественную ось и нормируем, чтобы система не "разлетелась" cy[i] = (alpha * cosf(phase)) / NY; } // 3. Вызов функции calculate // Прототип: void calculate(int n, float *out, int nx, float *x, float *cx, int ny, float *y, float *cy); calculate(N, out, x, cx, NX, y, cy, NY); // 4. Вывод на экран printf("Coefficients CX:\n"); for (int i = 0; i < NX; i++) printf("%.4f ", cx[i]); printf("\n\nCoefficients CY:\n"); for (int i = 0; i < NY; i++) printf("%.4f ", cy[i]); printf("\n\nResults (Index, Value):\n"); for (int i = 0; i < N; i++) { printf("%d, %.6f\n", i, out[i]); } // 5. Вывод в CSV файл FILE *f = fopen("output.csv", "w"); if (f) { fprintf(f, "Type,Index,Value\n"); for (int i = 0; i < NX; i++) fprintf(f, "CX,%d,%.6f\n", i, cx[i]); for (int i = 0; i < NY; i++) fprintf(f, "CY,%d,%.6f\n", i, cy[i]); for (int i = 0; i < N; i++) fprintf(f, "OUT,%d,%.6f\n", i, out[i]); fclose(f); printf("\nData saved to output.csv\n"); } return 0;}
Создаём файл Main.c и сохраняем
После чего конечно же мы не будем долго думать как создать Makefile для gcc
Makefile
Хотеть Makefile для антилопы а также Си не хуже 2011 года, сканировать все файлы с и компилировать в оут исполняемый а также математика либа
# Настройки компилятораCC = gccCFLAGS = -Wall -Wextra -std=c11 -O2LDFLAGS = -lm# Имя выходного файлаTARGET = out# Автоматический поиск всех .c файлов в текущей директорииSRCS = $(wildcard *.c)# Превращение списка .c в список .oOBJS = $(SRCS:.c=.o)# Главная цельall: $(TARGET)# Сборка исполняемого файла$(TARGET): $(OBJS) $(CC) $(OBJS) -o $(TARGET) $(LDFLAGS)# Правило для объектных файлов%.o: %.c $(CC) $(CFLAGS) -c $< -o $@# Очисткаclean: rm -f $(OBJS) $(TARGET).PHONY: all clean
запускаем make, видим
gcc -Wall -Wextra -std=c11 -O2 -c calculate.c -o calculate.o
gcc -Wall -Wextra -std=c11 -O2 -c Main.c -o Main.o
gcc calculate.o Main.o -o out -lm
запускаем исполняемый файл ./out
Вывод программы
Type,Index,Value
CX,0,0.200000
CX,1,0.200000
CX,2,0.200000
CX,3,0.200000
CX,4,0.200000
CY,0,0.000000
CY,1,0.049448
CY,2,-0.008871
CY,3,0.092645
CY,4,0.083334
CY,5,-0.053389
CY,6,-0.077179
OUT,0,0.200000
OUT,1,0.409890
OUT,2,0.618494
OUT,3,0.845476
OUT,4,1.090962
OUT,5,1.127226
OUT,6,1.138613
OUT,7,1.153176
OUT,8,1.149394
OUT,9,1.122531
OUT,10,1.102652
OUT,11,1.099363
OUT,12,1.094917
OUT,13,1.089723
OUT,14,1.089271
OUT,15,1.091743
….
Теперь приступим к визуализации результатов нашего csv. Также очищаем контекст для старта с нуля
Визуализатор Питон
хотеть питон визуализируй комплексная плоскость если коэффициенты отсчётов входа X как CX а выходов Y как СУ поля а на графике от отсчётов размести поле OUT. Читай csv формат тип CX или CY или OUT ,index,value. Индекс коэффициентов максимальный есть порядок фильтра. Пересчитать коэффициенты в нули и полюса для строить график комплексная плоскость и единичная окружность справа половина слева половина график отсчётов ОУТ от индекса. матплотлиб без пандас. открой любой csv папка текущая. данные идут списком в столбец, первый столбец тип, первая строка заголовок
import matplotlib.pyplot as pltimport numpy as npimport glob# 1. Чтение CSV (без pandas)csv_files = glob.glob("*.csv")if not csv_files: raise FileNotFoundError("CSV файл не найден в текущей директории.")filename = csv_files[0]cx_dict, cy_dict, out_dict = {}, {}, {}with open(filename, 'r') as f: next(f) # Пропуск заголовка for line in f: parts = line.strip().split(',') if len(parts) < 3: continue label, idx, val = parts[0], int(parts[1]), float(parts[2]) if label == 'CX': cx_dict[idx] = val elif label == 'CY': cy_dict[idx] = val elif label == 'OUT': out_dict[idx] = val# 2. Подготовка массивовdef to_arr(d): if not d: return np.array([1.0]) res = np.zeros(max(d.keys()) + 1) for k, v in d.items(): res[k] = v return resb = to_arr(cx_dict) # Числитель (нули)a = to_arr(cy_dict) # Знаменатель (полюса)# 3. Расчет нулей и полюсовzeros = np.roots(b) if len(b) > 1 else []poles = np.roots(a) if len(a) > 1 else []# 4. Визуализацияfig, (ax_out, ax_z) = plt.subplots(1, 2, figsize=(12, 5))# Слева: График отсчетов OUTindices = sorted(out_dict.keys())values = [out_dict[i] for i in indices]ax_out.stem(indices, values)ax_out.set_title("Отсчеты OUT")ax_out.grid(True)# Справа: Комплексная плоскостьunit_circle = plt.Circle((0,0), 1, color='black', fill=False, linestyle='--', alpha=0.5)ax_z.add_artist(unit_circle)ax_z.scatter(np.real(zeros), np.imag(zeros), s=60, marker='o', edgecolors='blue', facecolors='none', label='Нули (CX)')ax_z.scatter(np.real(poles), np.imag(poles), s=60, marker='x', color='red', label='Полюса (CY)')ax_z.axhline(0, color='black', lw=1)ax_z.axvline(0, color='black', lw=1)ax_z.set_xlim([-1.5, 1.5])ax_z.set_ylim([-1.5, 1.5])ax_z.set_aspect('equal')ax_z.set_title("Комплексная плоскость")ax_z.legend()ax_z.grid(True)plt.tight_layout()plt.show()

Ну что-ж, довольно неплохо вайб-ЦОС позволяет анализировать различные фильтры, хорошо видна КИХ-составляющая вначале пятого порядка (5 отсчётов) и БИХ с затуханием, построена единичная окружность, нанесены нули-полюса.
Далее следуют небольшие примеры для методов с конечными разностями
Столкновение Тейи и Земли. Луна не получилась
Язык Змей Горыныч модель окружность 1 окружность 2 навстречу друг другу диаметры отношение один к четырём окружность есть 64 точки отрезков аппроксимация каждый отрезок масса отрезки связаны в точках использовать сохранение импульса закон Ньютона столкновение без рассеяния отрезки между собой закон Гука пружинят конечные разности расчёт шаг счёта по времени примерно одна окружность в секундe использовать матплотлиб визуализация мышь слайдер быстрее медленнее логарифм
import numpy as npimport matplotlib.pyplot as pltfrom matplotlib.widgets import Sliderfrom matplotlib.animation import FuncAnimation# Параметры системыN = 64 # Количество точек в окружностиK_SPRING = 5000.0 # Жесткость пружинMASS = 0.1 # Масса одной точкиR1, R2 = 1.0, 4.0 # Радиусы (1:4)V_INIT = 2.5 # Начальная скорость сближения# Инициализация координат и скоростейtheta = np.linspace(0, 2 * np.pi, N, endpoint=False)# Окружность 1 (Малая)pos1 = np.stack([R1 * np.cos(theta) - 7.0, R1 * np.sin(theta)], axis=1)vel1 = np.zeros_like(pos1)vel1[:, 0] = V_INIT# Окружность 2 (Большая)pos2 = np.stack([R2 * np.cos(theta) + 7.0, R2 * np.sin(theta)], axis=1)vel2 = np.zeros_like(pos2)vel2[:, 0] = -V_INIT# Длины покоя (отрезки аппроксимации)def get_lengths(p): diff = np.roll(p, -1, axis=0) - p return np.sqrt(np.sum(diff**2, axis=1))L1, L2 = get_lengths(pos1), get_lengths(pos2)# Визуализацияfig, ax = plt.subplots(figsize=(10, 6))plt.subplots_adjust(bottom=0.2)line1, = ax.plot([], [], 'r-o', markersize=3, lw=1.5, label='Small (1x)')line2, = ax.plot([], [], 'b-o', markersize=3, lw=1.5, label='Large (4x)')ax.set_xlim(-12, 12)ax.set_ylim(-6, 6)ax.set_aspect('equal')ax.legend()# Слайдер для скорости (логарифмический шаг)ax_speed = plt.axes([0.25, 0.05, 0.5, 0.03])slider = Slider(ax_speed, 'Time Step (log)', -4.0, -1.5, valinit=-3.0)def compute_physics(p, v, L_rest): # Силы упругости (Закон Гука) diff = np.roll(p, -1, axis=0) - p dist = np.sqrt(np.sum(diff**2, axis=1)) f_mag = K_SPRING * (dist - L_rest) f_vec = (diff / dist[:, None]) * f_mag[:, None] # Результирующая сила на каждую точку (соседние сегменты) net_f = f_vec - np.roll(f_vec, 1, axis=0) # Ускорение (Закон Ньютона) a = net_f / MASS return adef update(frame): global pos1, vel1, pos2, vel2 # Текущий шаг времени из слайдера dt = 10**slider.val # Расчет сил и обновление (метод Эйлера-Кромера для стабильности) for _ in range(5): # Вложенные итерации для точности при больших dt # Физика связей vel1 += compute_physics(pos1, vel1, L1) * dt vel2 += compute_physics(pos2, vel2, L2) * dt # Обработка столкновения (Сохранение импульса) # Проверка расстояния между всеми парами точек окружностей dist_matrix = np.linalg.norm(pos1[:, None, :] - pos2[None, :, :], axis=2) collisions = np.where(dist_matrix < 0.25) for i, j in zip(*collisions): normal = (pos1[i] - pos2[j]) / dist_matrix[i, j] v_rel = vel1[i] - vel2[j] v_norm = np.dot(v_rel, normal) if v_norm < 0: # Обмен импульсами (массы равны, без рассеяния) impulse = v_norm * normal vel1[i] -= impulse vel2[j] += impulse pos1 += vel1 * dt pos2 += vel2 * dt # Обновление графики (замыкаем кольцо для отрисовки) p1_plot = np.vstack([pos1, pos1[0]]) p2_plot = np.vstack([pos2, pos2[0]]) line1.set_data(p1_plot[:, 0], p1_plot[:, 1]) line2.set_data(p2_plot[:, 0], p2_plot[:, 1]) return line1, line2ani = FuncAnimation(fig, update, interval=20, blit=True)plt.show()
Как обычно запускаем ранее навайбкоженным ./generate_requirements.sh zmei.py с автоматической подцепкой виртуального окружения

Робот-манипулятор двузвенный. Используется матмодель, которой надо дать инъекцию в виде подсказок про арктангенс двойного аргумента (от 0 до 360) и sqrt — теорему Пифагора, а также что одной точке может соответствовать 2 положения (плюс минус дискриминант), которых тем больше чем больше звеньев.
Автоботы и Десептиконы
Пропитонь двузвенный манипулятор да дай-ка в него PyGame numpy но будь осторожен с дискриминант sqrt а угол atan2 клавиатура меняй знак положение. Мышь точка задания фиксация первого звена центр координаты ортогональные сцены сетка имеется. Ай да обратная кинематика! Ух сольвер тригонометрический.
import pygameimport numpy as npimport math# Константы сценыWIDTH, HEIGHT = 800, 600L1, L2 = 180, 140 # Длины звеньевORIGIN = np.array([WIDTH // 2, HEIGHT // 2])def solve_ik(target, l1, l2, sign): tx, ty = target dist_sq = tx**2 + ty**2 dist = math.sqrt(dist_sq) # Ограничение рабочей зоны if dist > l1 + l2: dist_sq = (l1 + l2)**2 dist = l1 + l2 # Закон косинусов для второго угла cos_a2 = (dist_sq - l1**2 - l2**2) / (2 * l1 * l2) cos_a2 = np.clip(cos_a2, -1, 1) # Защита от ошибок в sqrt # Выбор знака локтя через atan2 sin_a2 = math.sqrt(1 - cos_a2**2) * sign a2 = math.atan2(sin_a2, cos_a2) # Угол первого звена a1 = math.atan2(ty, tx) - math.atan2(l2 * sin_a2, l1 + l2 * cos_a2) return a1, a2def run(): pygame.init() screen = pygame.display.set_mode((WIDTH, HEIGHT)) clock = pygame.time.Clock() elbow_sign = 1 while True: for event in pygame.event.get(): if event.type == pygame.QUIT: return if event.type == pygame.KEYDOWN: if event.key == pygame.K_SPACE: elbow_sign *= -1 screen.fill((30, 30, 30)) # Сетка for i in range(0, WIDTH, 40): pygame.draw.line(screen, (50,50,50), (i,0), (i,HEIGHT)) for i in range(0, HEIGHT, 40): pygame.draw.line(screen, (50,50,50), (0,i), (WIDTH,i)) # Таргет от мышки относительно центра m_pos = np.array(pygame.mouse.get_pos()) - ORIGIN # Расчет a1, a2 = solve_ik(m_pos, L1, L2, elbow_sign) # Координаты суставов joint = ORIGIN + np.array([L1 * math.cos(a1), L1 * math.sin(a1)]) end_eff = joint + np.array([L2 * math.cos(a1 + a2), L2 * math.sin(a1 + a2)]) # Отрисовка (Ортогонально из центра) pygame.draw.line(screen, (0, 255, 0), ORIGIN, joint, 6) # Плечо pygame.draw.line(screen, (255, 100, 0), joint, end_eff, 6) # Предплечье pygame.draw.circle(screen, (255, 255, 255), ORIGIN, 8) pygame.draw.circle(screen, (200, 0, 0), end_eff.astype(int), 5) pygame.display.flip() clock.tick(60)if __name__ == "__main__": run()

Пробелом можно менять положение локтя, мышью — крутить роботом обеспечивая положение звеньев
Вайб-код-стих. Пропарсим с использованием питона C-файлы сделанные выше и выберем имена функций.
Рифмо-парсер
Питоном я доволен
Файл открыть любой
Там С код стройный
Читай его любой
Используй regexp дорогой
И извлекай Имя функции () как родной
Те что в скобках и
Фигурных расписных
Строй список мне имён
Довольный и на экран выводи
Мой файл пай мне задай
Ищем мы всё в каталоге текущем
Всё что Си имеет текст грядущий
Но ты за ключевым словом следи
Всякие форы и ифы убери!
Устроит меня не имплементация
А только реализация
Результат:
import reimport os# Регулярка: ищет потенциальное имя функции перед скобками,# игнорируя ключевые слова C, и проверяет наличие открывающей фигурной скобкиpattern = r'\b(?!if|while|for|switch|return|void|int|char|float|double|static|struct|else)\w+\s*\([^)]*\)\s*\{'def find_functions(): # Ищем файлы с расширением .c в текущей папке c_files = [f for f in os.listdir('.') if f.endswith('.c')] if not c_files: print("Си-файлов в округе не видать...") return for filename in c_files: print(f"--- Файл: {filename} ---") try: with open(filename, 'r', encoding='utf-8') as f: content = f.read() # Находим все совпадения matches = re.findall(pattern, content) for match in matches: # Очищаем имя от скобок и лишних символов func_name = match.split('(')[0].strip() # На случай, если в match попал тип данных (например, "int main") func_name = func_name.split()[-1] print(f"Найдена функция: {func_name}") except Exception as e: print(f"Ошибка при чтении {filename}: {e}")if __name__ == "__main__": find_functions()
В созданных выше файлах он прекрасно находит наши функции
— Файл: Main.c —
Найдена функция: main
— Файл: calculate.c —
Найдена функция: calculate
Практически все модели обладают +- одинаковым выводом, так как обучающие выборки наверняка в большинстве своём примерно одни и те же, собраны из исходников, форумов и подготовлены по известным шаблонам датасетов. Промпты рабочие с первого раза за редким исключением.
ссылка на оригинал статьи https://habr.com/ru/articles/1028834/