Насколько я успел понять, написание программ с помощью нейросетей сейчас "в тренде", причем как со стороны профессионалов, так и со стороны дилетантов. Первые используют нейросети для освобождения от рутины, а вторые - потому что сами писать не могут, а хочется показать результат.
Данная статья написана "по мотивам" этой публикации и обсуждений в комментариях к ней. Я никак не планирую использовать нейросети в своей деятельности, но мне стало интересно, насколько удобоваримый результат можно получить с помощью бесплатной нейросети.
Хочу отметить, что мой эксперимент был интересен именно мне, но я не стал публиковать эту статью на своем сайте, ибо там нет возможности обсуждения, да и назначение моего "компьютерного" сайта несколько иное. Ну и, как отметил выше, эта статья есть некий "ответ" на статью другого автора тут на Хабре.
Что же я хотел? Я хотел сравнить свои программы и программы, написанные ИИ. Но эксперимент должен быть серьезным, поэтому статья ожидается достаточно большой, особенно с учетом публикации кодов...
Итак, я обратился к чатужпт с вопросом, какую сеть он посоветует. Посоветовал себя, я и согласился. Среди перечисленных им языков были Питон и разные виды Си. Но Си я не знаю и на нем не пишу. Я уже больше 30 лет пишу на Бейсике разных видов (от qb4 до VBNET), но Бейсика в списке не было. Зато был Питон. Это интересно как раз тем, что Питон я мельком изучал и программы на нем писал в качестве тренировки. Даже две статьи про свое обучение написал, а тексты программ есть там же в разделе "Исходники".
Но считать себя знатоком Питона никак не могу и тут как раз подходит вайб-кодинг: язык я знаю плохо и типа мне нужна помощь. Но все же код могу понять. Понятно, что мои программы на Питоне дилетантские и неоптимальные. Тем интереснее было посмотреть, что выдаст нейронка и кто из нас в итоге победит! :)
Ну и нейросети, как я понял, как раз заточены на Питон и по идее результат должен превзойти мой на порядок. Да и для Виндовс кроме Питона и разных версий Бейсика у меня примеров и нет. Так что выбор был предрешен. Я решал три задачи. Расскажу поочередно.
1. "Напиши программу на Питоне, которая печает на консоли свой код".
Ну, ЖПТ обрадовался, что это "известная задача под названием quine" и на Питоне она имеет очень короткое решение в виде
s = 's = %r; print(s%%s)'; print(s%s)
Тут я почувствовал себя дураком, потому что мой код (формальным переводом с языка sb1) был таким:
K=chr(34)T=['']*9T[0]="K=chr(34)"T[1]="T=['']*9"T[2]="for J in range(0,2):"T[3]=" print(T[J])"T[4]="for J in range(0,9):"T[5]=" print(chr(84)+chr(91)+str(J)+chr(93)+chr(61)+K+T[J]+K)"T[6]="for J in range(2,9):"T[7]=" print(T[J])"T[8]="input(chr(13))"for J in range(0,2): print(T[J])for J in range(0,9): print(chr(84)+chr(91)+str(J)+chr(93)+chr(61)+K+T[J]+K)for J in range(2,9): print(T[J])input(chr(13))
Признав свое поражение, я приступил ко второй задаче.
2. "Напиши программу, которая выводит окно размером 200*100 с белым фоном, где черным цветом выводится текущее время. Размер окна можно менять как угодно и вывод времени должен вписываться в окно в любом случае путем изменения размера шрифта".
Я не уверен, что дословно промпт был точно таким, но задача такая.
Как ни удивительно, но ЖПТ сделал такую программу, долго расхваливая как плавно она работает. Должен признать, что чисто визуально она действительно работает плавнее моей, но я просто не стал с этим заморачиваться, когда писал свою: в принципе, вполне мог бы. Однако, она тоже имеет свои минусы, в т.ч. слишком большие поля. Ну да ладно.
Кстати, эта задача не абстрактная: мне нужны были именно такие часы и соответствующую программу (не на Питоне, а на VBNET) я запускаю каждый день во время своих йоговских практик, да и не только.
Приведу здесь код ИИ и свой код тоже для сравнения.
# ЧАСЫ, код нейронкиimport tkinter as tkfrom tkinter import fontimport timePADDING = 10class Clock: def __init__(self, root): self.root = root root.title("Часы") root.geometry("200x100") root.configure(bg="white") self.label = tk.Label(root, text="", bg="white", fg="black") self.label.place(relx=0.5, rely=0.5, anchor="center") self.current_time = "" root.bind("<Configure>", self.on_resize) self.update_time() def fit_font(self): w = max(1, self.root.winfo_width() - 2 * PADDING) h = max(1, self.root.winfo_height() - 2 * PADDING) low = 1; high = 500; best = 1 while low <= high: mid = (low + high) // 2 f = font.Font(family="Arial", size=mid, weight="bold") text_w = f.measure(self.current_time) text_h = f.metrics("linespace") if text_w <= w and text_h <= h: best = mid; low = mid + 1 else: high = mid - 1 self.label.configure(font=("Arial", best, "bold")) def update_time(self): self.current_time = time.strftime("%H:%M:%S") self.label.config(text=self.current_time) self.fit_font() self.root.after(1000, self.update_time) def on_resize(self, event): self.fit_font()root = tk.Tk()Clock(root)root.mainloop()
# ЧАСЫ, код мой из "Исходников"import datetimeimport threadingimport sysimport mathfrom tkinter import *frmHASY=Tk()frmHASY.title("Текущее время")frmHASY.geometry('300x60')frmHASY.configure(bg='#FFFFFF')METKA=Label(frmHASY, font=('Courier New', 40, 'bold'), background='#FFFFFF', foreground='#000000')METKA.place(relx=0.5, rely=0.5, anchor=CENTER)def OnTimer(): try: W=frmHASY.winfo_width()/7 H=frmHASY.winfo_height() M=min(W,H) RAZ=math.floor(M) METKA.config(font=('Courier New', RAZ, 'bold')) STRO=datetime.datetime.now().time().strftime("%H:%M:%S") METKA.config(text=STRO) VREM=threading.Timer(1.0, OnTimer).start() except: sys.exit() OnTimer()frmHASY.mainloop()
Как видно, количество строк у меня вдвое меньше, при том, что я модернизировал код ИИ, убрав лишние пустые строки и разместив некоторые операторы через ";" в одну строку. Просто для компактности представления.
Сравнить коды мне сложно (помимо размера) и эту функцию я отдаю профессионалам в Питоне. :) Просто замечу, что я свой код тоже "переводил" с Бейсика, что видно даже по именам переменных. Понимаю, что в целом такой путь порочен, но я только знакомился с языком и этот путь был самым простым. И получилось же!
А вот с третьей задачей даже не знаю как быть в плане описания... Будет длинно!
3. Разумеется, тут я выбрал уже достаточно сложную задачу, к тому же имеющую некие "подводные камни", о чем далее.
Поначалу я обратился к чатужпт даже без авторизации (учтите, что чисто хронологически эту задачу я решал ДО перечисленных выше). Задачу эту я тоже выбрал из своего раздела "Исходники", там разные задачи на разных языках реализованные.
Какая задача выбрана? Я, конечно, не смогу тут привести мой достаточно длинный диалог с чатом, но отмечу основные моменты.
Задача была такая: Окно 640*480, синий фон. Вращаются два желтых кольца справа налево и сверху вниз, синхронно. Это один режим. Переключение на второй - клавишей "Enter".
Второй режим. Черный фон окна и из центра растут круги стандартных цветов: красный, желтый, зеленый, голубой, синий и фиолетовый. Потом снова красный и т.д. Этот режим имеет два варианта (переключение клавишей "Space"):
Один - новые круги рисуются поверх старых, яркость кругов уменьшается от центра к периферии. Второй - в ходе вывода кругов все изображение темнеет вплоть до черного, поэтому каждый цвет рисуется как бы на черном фоне.
На самом деле, я упростил задачу для нейросети, поскольку изначально скорость разных цветов предполагалась разная. Но уж ладно... Изначально ЖПТ сказал, что нет проблем, он подключит то, и то, и вообще сделает 3D-эффекты. Я сказал, что можно использовать только стандартные библиотеки Питона и никаких 3Д не нужно. Что меня удивило - так это сразу замечание ЖПТ о тех подводных камнях, через которые прошел я сам при написании программы: я привык, что можно просто рисовать окружности нужным цветом и потом поверх рисовать фоном. И так "двигать" кольца. Но на Питоне это все объекты и ресурсы улетают.
ЖПТ предложил заранее создать кольца и таки просто двигать их (я к этому варианту пришел, но далеко не сразу). Вообще он по ходу обсуждения много чего предлагал: иногда лишнего, иногда полезного, но в целом задачу понял вроде бы правильно, что меня тоже удивило, ибо задача мне казалась непривычной.
Поскольку я писал эту программу по ходу изучения Питона, то много раз переделывал свой код как из-за прямых ошибок, так и из-за выбора неоптимальных методов (например, в движении колец). Но мне простительно, я этот язык не знал! А вот ЖПТ, выдавая код по частям в силу ограничений на размер сообщения, по ходу выдачи менял прошлый текст и просил "заменить вот этот фрагмент на вот этот", ибо "так лучше" или "тот вообще не работает". Но это странно, ведь по идее ЖПТ Питон знает! Или как?
В общем, в итоге я получил код раза в три больше своего и на мой взгляд, сложнее. Но! Мой код "некошерный", т.е. это "лобовой" перевод кода с Бейсика на Питон с изменением соответствующих частей в силу разности организации (пример - работа с кольцами). Тем не менее, мой код работает верно, а код ЖПТ - нет. О чем я ему злорадно и сообщил, отметив, что человек, даже будучи дилетантом, сделал лучше.
ИИ признал ошибки, отметив, что "сделал их по невнимательности". Но позвольте! Роботы для того и нужны, чтобы быть полностью внимательными! Допускать ляпы (типа "да, я забыл вначале указать цвет окна, поэтому оно белое") - это удел человека. А тут... Но главное - неверный выбор алгоритма кругов ("да, ты прав, надо было сделать 256 кругов по 1 пикселю") и сбой в циклах цветов. Но это я даже не пытался анализировать и просить исправить, ибо эксперимент и так показал главное: человек лучше! И рассчитывать на ИИ нельзя.
Вообще мой эксперимент включал не только проверку возможности нейросети в программировании, но и проверку уровня сложности донесения до ИИ требуемой задачи. Но сами понимаете, повторить тут весь диалог (довольно долгий!) невозможно. Приведу тут код, полученный от ИИ (он очень длинный, поэтому загнал в спойлер):
Скрытый текст
#!/usr/bin/env python3# -*- coding: utf-8 -*-"""первый вариант без авторизацииRings & WavesДемонстрационная программа на tkinter.Enter - переключение между режимами.Space - переключение варианта волн.Esc - выход.Только стандартная библиотека Python."""import mathimport timeimport tkinter as tk# ============================================================# Настраиваемые параметры# ============================================================WINDOW_WIDTH = 640WINDOW_HEIGHT = 480CENTER_X = WINDOW_WIDTH // 2CENTER_Y = WINDOW_HEIGHT // 2FPS = 25FRAME_TIME = int(1000 / FPS)BACKGROUND_BLUE = "#0030C0"BACKGROUND_BLACK = "#000000"RING_COLOR = "#FFD800"RING_WIDTH = 4RING_RADIUS = 120ROTATION_PERIOD = 2.0RAINBOW = ( "#ff0000", "#ffff00", "#00ff00", "#00ffff", "#0000ff", "#a000ff",)MAX_RADIUS = int(math.hypot(CENTER_X, CENTER_Y)) + 20WAVE_SPEED = 180.0 # пикселей в секундуWAVE_INTERVAL = 0.6 # секунд между волнамиMAX_WAVES = 12GRADIENT_STEPS = 16# ============================================================# Вспомогательные функции# ============================================================def clamp(v, a, b): if v < a: return a if v > b: return b return vdef darken(color, k): """ Затемнение цвета. k = 1.0 -> исходный цвет k = 0.0 -> чёрный """ k = clamp(k, 0.0, 1.0) r = int(color[1:3], 16) g = int(color[3:5], 16) b = int(color[5:7], 16) r = int(r * k) g = int(g * k) b = int(b * k) return f"#{r:02x}{g:02x}{b:02x}"# ============================================================# Волна# ============================================================class Wave: def __init__(self, color): self.color = color self.radius = 0.0 self.alive = True self.items = [] def update(self, dt): self.radius += WAVE_SPEED * dt if self.radius > MAX_RADIUS: self.alive = False def draw(self, canvas): if not self.items: for item in self.items: canvas.itemconfigure(item, state="normal") r = self.radius for i in range(GRADIENT_STEPS): k = (GRADIENT_STEPS - i) / GRADIENT_STEPS color = darken(self.color, k * k) item = canvas.create_oval( CENTER_X - r, CENTER_Y - r, CENTER_X + r, CENTER_Y + r, outline="", fill=color, ) self.items.append(item) r -= self.radius / GRADIENT_STEPS else: r = self.radius step = self.radius / GRADIENT_STEPS for i, item in enumerate(self.items): k = (GRADIENT_STEPS - i) / GRADIENT_STEPS color = darken(self.color, k * k) canvas.coords( item, CENTER_X - r, CENTER_Y - r, CENTER_X + r, CENTER_Y + r, ) canvas.itemconfigure(item, fill=color) r -= step if r <= 0: break def destroy(self, canvas): for item in self.items: canvas.itemconfigure(item, state="hidden") self.items.clear()# ============================================================# Основной класс# ============================================================class App: def __init__(self): self.root = tk.Tk() self.root.title("Rings & Waves") self.root.resizable(False, False) self.canvas = tk.Canvas( self.root, width=WINDOW_WIDTH, height=WINDOW_HEIGHT, highlightthickness=0, ) self.canvas.pack() self.mode = 0 # 0 - кольца; 1 - волны self.wave_mode = 0 # 0 - одна волна; 1 - поток self.phase = 0.0 self.last_time = time.perf_counter() self.last_wave = self.last_time self.waves = [] self.root.bind("<Return>", self.on_enter) self.root.bind("<space>", self.on_space) self.root.bind("<Escape>", lambda e: self.root.destroy()) # Создаём два кольца один раз self.ring_h = self.canvas.create_oval( 0, 0, 0, 0, outline=RING_COLOR, width=RING_WIDTH ) self.ring_v = self.canvas.create_oval( 0, 0, 0, 0, outline=RING_COLOR, width=RING_WIDTH ) self.animate() self.root.mainloop() def clear_waves(self): for wave in self.waves: wave.destroy(self.canvas) self.waves.clear() def on_enter(self, event=None): self.mode ^= 1 if self.mode == 0: self.canvas.configure(bg=BACKGROUND_BLUE) self.canvas.itemconfigure(self.ring_h, state="normal") self.canvas.itemconfigure(self.ring_v, state="normal") self.clear_waves() else: self.canvas.configure(bg=BACKGROUND_BLACK) self.canvas.itemconfigure(self.ring_h, state="hidden") self.canvas.itemconfigure(self.ring_v, state="hidden") self.clear_waves() self.last_wave = time.perf_counter() def on_space(self, event=None): self.wave_mode ^= 1 self.clear_waves() def draw_rings(self): t = time.perf_counter() angle = (t % ROTATION_PERIOD) / ROTATION_PERIOD angle *= 2.0 * math.pi k = abs(math.cos(angle)) # Чтобы кольцо никогда полностью не исчезало k = max(k, 0.03) rx = RING_RADIUS ry = int(RING_RADIUS * k) self.canvas.coords( self.ring_h, CENTER_X - rx, CENTER_Y - ry, CENTER_X + rx, CENTER_Y + ry ) rx = int(RING_RADIUS * k) ry = RING_RADIUS self.canvas.coords( self.ring_v, CENTER_X - rx, CENTER_Y - ry, CENTER_X + rx, CENTER_Y + ry ) def spawn_wave(self): color = RAINBOW[len(self.waves) % len(RAINBOW)] self.waves.append(Wave(color)) def update_waves(self, dt): now = time.perf_counter() if self.wave_mode == 0: if not self.waves: self.spawn_wave() elif not self.waves[0].alive: self.clear_waves() self.spawn_wave() else: if (now - self.last_wave) >= WAVE_INTERVAL: self.last_wave = now self.spawn_wave() if len(self.waves) > MAX_WAVES: old = self.waves.pop(0) old.destroy(self.canvas) alive = [] for wave in self.waves: wave.update(dt) wave.draw(self.canvas) if wave.alive: alive.append(wave) else: wave.destroy(self.canvas) self.waves = alive def animate(self): now = time.perf_counter() dt = now - self.last_time self.last_time = now if self.mode == 0: self.draw_rings() else: self.update_waves(dt) self.root.after(FRAME_TIME, self.animate)if __name__ == "__main__": App()
Но это не все…
Вечером с другого компьютера(!) я авторизовался(!) в чате и после выполнения задач в пунктах 1 и 2 выше, я решил вернуться к данной теме.
И вот тут что интересно: по идее, ЖПТ не должен был знать, что “я тот” и “я этот” — одно лицо, как и хранить информацию о диалоге без авторизации. Но на деле все оказалось, видимо, не так.
Нет, он на сказал “привет, я помню”. Но задачу он усвоил значительно быстрее! Однако, он создал несколько вариантов последовательно, опять же, на ходу “придумывая” улучшения. Поскольку я предупредил о длине статьи, я приведу тут два его варианта, а в конце — свой, из “Исходников”. Кстати, тут я ему все же сказал, что скорость должна зависеть от цвета круга.
Вариант первый (с ошибками!).
Скрытый текст
# вариант с ошибкамиimport tkinter as tkimport mathimport time# НастройкиWIDTH = 640HEIGHT = 480FPS = 60FRAME = int(1000 / FPS)RING_PERIOD = 4.0BACKGROUND_BLUE = "#0000C0"COLORS = [ (255, 0, 0), # красный (255, 255, 0), # желтый (0, 255, 0), # зеленый (0, 255, 255), # голубой (0, 0, 255), # синий (255, 0, 255), # фиолетовый]COLOR_TIME = [3.0, 3.5, 4.0, 4.5, 5.0, 5.5,]class Demo: def __init__(self): self.root = tk.Tk() self.root.title("Demo") self.root.geometry(f"{WIDTH}x{HEIGHT}") self.canvas = tk.Canvas(self.root, width=WIDTH, height=HEIGHT, bg=BACKGROUND_BLUE, highlightthickness=0) self.canvas.pack(fill="both", expand=True) self.root.bind("<Return>", self.enter_pressed) self.root.bind("<space>", self.space_pressed) self.canvas.bind("<Configure>", self.resize) self.mode = 0 # 0 - кольца # 1 - круги с радиальным градиентом # 2 - круги с временным затуханием self.start_time = time.perf_counter() self.cx = WIDTH / 2; self.cy = HEIGHT / 2 self.radius = min(WIDTH, HEIGHT) * 0.35 self.ring1 = self.canvas.create_oval(0, 0, 0, 0, outline="yellow", width=2) self.ring2 = self.canvas.create_oval(0, 0, 0, 0, outline="yellow", width=2) self.color_index = 0 self.circle_radius = 0 self.circle_items = [] self.last_radius = -1 self.mode_start = time.perf_counter() self.animate() self.root.mainloop() def resize(self, event): self.cx = event.width / 2; self.cy = event.height / 2 self.radius = min(event.width, event.height) * 0.35 def enter_pressed(self, event): if self.mode == 0: self.mode = 1 self.mode_start = time.perf_counter() self.color_index = 0 self.prepare_circle() else: self.mode = 0 self.canvas.delete("all") self.canvas.configure(bg=BACKGROUND_BLUE) self.ring1 = self.canvas.create_oval(0,0,0,0, outline="yellow", width=2) self.ring2 = self.canvas.create_oval(0,0,0,0, outline="yellow", width=2) def space_pressed(self, event): if self.mode == 0: return if self.mode == 1: self.mode = 2 else: self.mode = 1 self.mode_start = time.perf_counter() self.color_index = 0 self.prepare_circle() def animate(self): if self.mode == 0: self.animate_rings() else: self.animate_circles() self.root.after(FRAME, self.animate) def animate_rings(self): t = time.perf_counter() - self.start_time phase = 2 * math.pi * t / RING_PERIOD c = abs(math.cos(phase)) rx1 = self.radius; ry1 = self.radius * c rx2 = self.radius * c; ry2 = self.radius self.canvas.coords(self.ring1, self.cx - rx1, self.cy - ry1, self.cx + rx1, self.cy + ry1) self.canvas.coords(self.ring2, self.cx - rx2, self.cy - ry2, self.cx + rx2, self.cy + ry2) def rgb(self, r, g, b): return "#{:02x}{:02x}{:02x}".format( max(0, min(255, int(r))), max(0, min(255, int(g))), max(0, min(255, int(b))) ) def prepare_circle(self): self.canvas.delete("all") self.canvas.configure(bg="black") self.circle_items.clear() self.last_radius = -1 self.mode_start = time.perf_counter() def next_color(self): self.color_index += 1 if self.color_index >= len(COLORS): self.color_index = 0 self.prepare_circle() def animate_circles(self): elapsed = time.perf_counter() - self.mode_start duration = COLOR_TIME[self.color_index] progress = elapsed / duration if progress >= 1.0: self.next_color(); return max_radius = self.radius radius = int(progress * max_radius) if radius <= self.last_radius: return base = COLORS[self.color_index] while self.last_radius < radius: self.last_radius += 1 r = self.last_radius if self.mode == 1: # Радиальный градиент k = 1.0 - r / max_radius else: # Временное затухание k = 1.0 - progress if k < 0: k = 0 color = self.rgb(base[0] * k, base[1] * k, base[2] * k) item = self.canvas.create_oval( self.cx - r, self.cy - r, self.cx + r, self.cy + r, outline=color, width=2 ) self.circle_items.append(item)Demo()
Во-первых, тут нет выхода по эскейпу. Во-вторых, варианты 1 и 2 кругов ничем визуально не отличаются и работают неверно. Я не буду разбирать код и искать ошибки (я же дилетант по условию задачи!). Но я указал ИИ, что код не работает как надо. Получил массу извинений и предложеений по улучшению. В итоге ИИ дал другой вариант, гораздо более компактный и как ни странно, в целом верный.
Мне пришлось добавить (уже самому) выход по эскейпу, а также убрать строку, которую он все же оставил. Про эскейп я не говорил, а вот лишняя строка - это все же ошибка! В коде программы есть комментарий, поймете.
И еще: я уменьшил число строк, убрав лишние пустые и записав вызовы с многими параметрами в одну строку (он каждый параметр указывал с новой строки). Лично мне удобнее читать код так.
Код исправленной программы:
Скрытый текст
# hind by chatgptimport tkinter as tkimport math,timeWIDTH,HEIGHT=640,480FPS=60FRAME=int(1000/FPS)RING_PERIOD=4.0COLORS=[(255,0,0),(255,255,0),(0,255,0),(0,255,255),(0,0,255),(255,0,255)]TIMES=[3, 3.5, 4, 4.5, 5, 5.5]def rgb(r,g,b): return f"#{int(max(0,min(255,r))):02x}{int(max(0,min(255,g))):02x}{int(max(0,min(255,b))):02x}"class Demo: def __init__(self): self.root=tk.Tk() self.root.geometry(f"{WIDTH}x{HEIGHT}") self.cv=tk.Canvas(self.root,bg="#0040c0",highlightthickness=0) self.cv.pack(fill="both",expand=True) self.cx,self.cy=WIDTH/2,HEIGHT/2 self.R=min(WIDTH,HEIGHT)*0.35 self.r1=self.cv.create_oval(0,0,0,0,outline="yellow",width=2) self.r2=self.cv.create_oval(0,0,0,0,outline="yellow",width=2) self.mode=0 self.var=0 self.start=time.perf_counter() self.mode_start=self.start self.items=[] self.last=-1 self.color=0 self.root.bind("<Configure>",self.resize) self.root.bind("<Return>",self.enter) self.root.bind("<space>",self.space) self.root.bind("<Escape>",lambda e: self.root.destroy()) # ДОБАВЛЕНО МНОЙ self.loop() self.root.mainloop() def resize(self,e): self.cx=e.width/2; self.cy=e.height/2; self.R=min(e.width,e.height)*0.35 def enter(self,e): if self.mode==0: self.mode=1; self.cv.delete("all"); self.cv.configure(bg="black") self.items=[]; self.last=-1; self.color=0; self.mode_start=time.perf_counter() else: self.mode=0; self.cv.delete("all"); self.cv.configure(bg="#0040c0") self.r1=self.cv.create_oval(0,0,0,0,outline="yellow",width=2) self.r2=self.cv.create_oval(0,0,0,0,outline="yellow",width=2) def space(self,e): if self.mode: self.var=1-self.var self.cv.delete("all"); self.items=[]; self.last=-1; self.color=0; self.mode_start=time.perf_counter() def loop(self): if self.mode==0:self.rings() else:self.circles() self.root.after(FRAME,self.loop) def rings(self): t=(time.perf_counter()-self.start)/RING_PERIOD*2*math.pi c=abs(math.cos(t)) self.cv.coords(self.r1,self.cx-self.R,self.cy-self.R*c,self.cx+self.R,self.cy+self.R*c) self.cv.coords(self.r2,self.cx-self.R*c,self.cy-self.R,self.cx+self.R*c,self.cy+self.R) def circles(self): dur=TIMES[self.color] p=(time.perf_counter()-self.mode_start)/dur if p>=1: self.color=(self.color+1)%len(COLORS)# self.cv.delete("all"); ОШИБКА! - убрано мной self.items=[]; self.last=-1; self.mode_start=time.perf_counter(); return r=int(self.R*p) base=COLORS[self.color] if self.var==1: k=1-p col=rgb(base[0]*k,base[1]*k,base[2]*k) for it in self.items: self.cv.itemconfig(it,outline=col) while self.last<r: self.last+=1 rr=self.last if self.var==0: k=1-rr/self.R col=rgb(base[0]*k,base[1]*k,base[2]*k) it=self.cv.create_oval(self.cx-rr,self.cy-rr,self.cx+rr,self.cy+rr,outline=col,width=2) self.items.append(it)Demo()
Этот вариант (с моими дополнениями) уже работает как надо! И я хочу для сравнения привести свой код тоже. Хотя он, как и предыдущие, получен переводом с Бейсика.
Мой код:
Скрытый текст
# мой код из "Исходников"import timeimport sysfrom tkinter import *WIR=640; VYS=480OKNO=Tk()OKNO.title("HIND (Press <Enter> (and <Space>) or <Esc>)")OKNO.geometry(f'{WIR}x{VYS}')OKNO.configure(bg='#000000')X=int(WIR/2); Y=int(VYS/2)W=int(OKNO.winfo_screenwidth()/2); H=int(OKNO.winfo_screenheight()/2)W=W-X; H=H-YOKNO.geometry(f'+{W}+{H}')OKNO.resizable(False, False)POLE=Canvas(OKNO, width=WIR, height=VYS, bg="#000080")POLE.pack()PRIZ=-1; ZAL=0; R=min(X,Y)-6; KOF=X/YOVAL1=[0]*128OVAL2=[0]*128OVAL3=[0]*128def TISTOP(): POLE.delete('all') OKNO.destroy() sys.exit()def RGB(r,g,b): CC=f'#{r:02x}{g:02x}{b:02x}' return CCdef WREM(TTT): VREM = time.time() while time.time()-VREM<TTT: continuedef ROTOR(): global PRIZ,R,X,Y I=1; Z=1; B=R POLE.delete('all') YG0=Y-B; YG1=Y+B XV0=X-B; XV1=X+B coordG=X-R,YG0,X+R,YG1 coordV=XV0,Y-R,XV1,Y+R POLE.create_rectangle(0,0,WIR-1,VYS-1,fill="#000080",outline="#000080") OVALG=POLE.create_oval(coordG,outline="#FFFF00") OVALV=POLE.create_oval(coordV,outline="#FFFF00") while PRIZ<0: S=Z*I/9; I=I+S if S>0 and I>R or S<0 and I<1: Z = -Z if I<1: I = 1 B=R/I YG0=Y-B; YG1=Y+B XV0=X-B; XV1=X+B coordG=X-R,YG0,X+R,YG1 coordV=XV0,Y-R,XV1,Y+R POLE.coords(OVALG,coordG) POLE.coords(OVALV,coordV) TT=int(100/(1+I/B)) WREM(TT/1000) POLE.update()def SFERA(): global KOF,X,Y,ZAL,PRIZ,OVAL1,OVAL2,OVAL3 if PRIZ==0: return POLE.delete('all') QQ="#000000" POLE.create_rectangle(0,0,WIR-1,VYS-1,fill=QQ,outline=QQ) for I in range(1,128): RAD=I*2; X3=X-I; Y3=Y-I OVAL1[I]=POLE.create_oval(X3,Y3,X3+RAD,Y3+RAD,outline=QQ) OVAL2[I]=POLE.create_oval(X3+1,Y3,X3+RAD-1,Y3+RAD,outline=QQ) OVAL3[I]=POLE.create_oval(X3,Y3+1,X3+RAD,Y3+RAD-1,outline=QQ) while PRIZ>0: for J in range(1,7): TIM=0.01*pow(KOF,J) for I in range(1,128): Q = (128 - I) *2 match J: case 1: QQ=RGB(Q,0,0) case 2: QQ=RGB(Q,Q,0) case 3: QQ=RGB(0,Q,0) case 4: QQ=RGB(0,Q,Q) case 5: QQ=RGB(0,0,Q) case 6: QQ=RGB(Q,0,Q) RAD=I*2; X3=X-I; Y3=Y-I if ZAL!=0: for K in range(1,I+1): POLE.itemconfig(OVAL1[K],outline=QQ) POLE.itemconfig(OVAL2[K],outline=QQ) POLE.itemconfig(OVAL3[K],outline=QQ) else: POLE.itemconfig(OVAL1[I],outline=QQ) POLE.itemconfig(OVAL2[I],outline=QQ) POLE.itemconfig(OVAL3[I],outline=QQ) WREM(TIM/1000) POLE.update() if PRIZ<=0: break if PRIZ<=0: breakdef KLAW(e): global PRIZ,ZAL KEY=e.keysym if KEY=="space" and PRIZ>0: ZAL=1-ZAL elif KEY=="Return": PRIZ=-PRIZ if PRIZ<0: ROTOR() elif PRIZ>0: SFERA() elif KEY=="Escape": PRIZ=0; TISTOP()OKNO.bind("<Key>", KLAW)OKNO.after(10,ROTOR)OKNO.mainloop()
Как я понял, ИИ писал программу по другой логике, чем я. Причем все три его варианта достаточно отличаются друг от друга.
Мне сложно оценивать качество результата (фактически это только третья программа ИИ, ибо два прошлых варианта работают неправильно). Конечно, я писал программу дольше, чем в сумме составляют все мои переговоры с ЖПТ. Правда, сколько писал - не помню (дело довольно давнее), а главное - я писал в ходе обучения! Читал документацию, спрашивал на форумах и т.д. Если бы я свободно владел Питоном, вполне вероятно, что написал бы программу быстрее, чем разговаривал с ИИ. Но достаточно свободно владею я только Бейсиком...
Так же понятно, что скорость генерации кода у ИИ в целом больше как минимум за счет скорости "набора текста" и прочих подобных моментов. Но в итоге эксперимента я все же могу утверждать, что "хоронить" программистов как профессию рано. :)
И еще мне кажется, что я понял одну важную особенность нейросетей: они видят локальные моменты, но не видят глобальной картины. Применительно к данной теме это означает, что маленькие программы пишутся ими быстрее и даже оптимальнее, чем напишет человек, ибо доступ к информации у человека заведомо беднее и труднее. Но в случае большой и сложной программы ИИ начинает давать сбои, потому что целое - не всегда есть просто сумма частей.
Этому есть достаточно точная и печальная аналогия в нашей жизни: современная медицина, как известно, рассматривает человека как совокупность органов, которыми занимаются разные узкие специалисты: кардиолог сердцем, флеболог сосудами, а гематолог кровью. Но нет никого, кто бы занимался всей этой системой как единым целым, не говоря о том, чтобы видеть весь организм полностью (как происходит в восточных медицинских моделях). И результаты такого порочного и ограниченного подхода лично я знаю не понаслышке...
Надеюсь, вы дочитали этот действительно большой текст до конца и надеюсь, что этот эксперимент был полезен, и результаты его интересны и тоже полезны. Что я не зря потратил время на его проведение, а вы - на чтение результатов. :)
P.S. Но вот чего я не понял, так это списка языков тут на Хабре. В списке для выбора кода есть крайне экзотические варианты, но нет ни Бейсика (в любом варианте!), ни даже Пролога! Нет Паскаля, хотя Дельфи есть, но все же это не синонимы. Я понимаю, что нет моего языка "Ellochka", но все остальные где?
ссылка на оригинал статьи https://habr.com/ru/articles/1054988/