Разработчик Frank Fiegel (Фрэнк Фигель) рассказал в блоге компании Glama о своём опыте использования GPT для деминификации чужого кода. Модель машинного обучения справилась с поставленной задачей и переписала минифицированный код на TypeScript.
Фигель нашёл на сайте Reactive Hackathon компонент с анимированным ASCII-артом, но не понял, как он реализован. Поэтому разработчик решил найти в коде сайта блок, который отвечает за анимацию. Исходник был найден, но он оказался минифицированным, поэтому с ходу понять принцип его работы было сложно.
Исходная анимация с сайтаМинифицированный кодconst { floor: ra, abs: KE, min: QE } = Math, O5 = [«reactive.network REACTIVE.NETWORK», «$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/|()1{}[]?-_+~<>i!lI;:,^`’. .:â-‘â-‘â-«â-^»], G7 = Date.now() % 3 ? O5[1] : O5[0], V5 = G7.length, JE = { fps: 60 }; function eT(e, t, n, r) { const i = t.time * 8e-5, s = QE(t.cols, t.rows), o = t.metrics.aspect * 0.2, l = { x: ((4 * (e.x — t.cols / 6.25)) / s) * o, y: (5 * (e.y — t.rows / 4)) / s }, u = ra(KE(YE(l) — i) * V5 + (ra(e.x / 1) % 2) * 2) % V5; return G7[u]; } const tT = () => { const e = j.useRef(null), [t, n] = j.useState({ height: null, width: null }); return ( j.useEffect(() => { function r() { n({ height: window.innerHeight, width: window.innerWidth }); } if (typeof window < «u») return n({ height: window.innerHeight, width: window.innerWidth }), window.addEventListener(«resize», r), () => window.removeEventListener(«resize», r); }, []), j.useEffect(() => { const r = e.current; if (!r) return; const i = 12, s = ra(t.width / i) * 1.6, o = ra(t.height / i), l = { aspect: s / o }, u = setInterval(() => { let c = «»; for (let d = 0; d < o; d++) { for (let f = 0; f < s; f++) c += eT({ x: f, y: d }, { cols: s, rows: o, metrics: l, time: Date.now() }); c += ` `; } r.textContent = c; }, 1e3 / JE.fps); return () => clearInterval(u); }, [t]), a.jsx(«div», { style: { position: «absolute», top: 0, left: 0, width: «100%», height: «100%» }, children: a.jsx(«div», { ref: e, style: { width: «100%», height: «100%», whiteSpace: «pre», overflow: «hidden» } }) }) ); }; function nT(e) { return Math.cos(e.x * e.x — e.y * e.y); } const { floor: ia, abs: rT, min: iT } = Math, D5 = [«reactive.network REACTIVE.NETWORK», «$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/|()1{}[]?-_+~<>i!lI;:,^`’. .:â-‘â-‘â-«â-^»], X7 = Date.now() % 3 ? D5[1] : D5[0], F5 = X7.length, sT = { fps: 60 }; function oT(e, t, n, r) { const i = t.time * 8e-5, s = iT(t.cols, t.rows), o = t.metrics.aspect * 0.2, l = { x: ((4 * (e.x — t.cols / 6.25)) / s) * o, y: (5 * (e.y — t.rows / 4)) / s }, u = ia(rT(nT(l) — i) * F5 + (ia(e.x / 1) % 2) * 2) % F5; return X7[u]; } const aT = () => { const e = j.useRef(null), [t, n] = j.useState({ height: null, width: null }); return ( j.useEffect(() => { function r() { n({ height: window.innerHeight, width: window.innerWidth }); } if (typeof window < «u») return n({ height: window.innerHeight, width: window.innerWidth }), window.addEventListener(«resize», r), () => window.removeEventListener(«resize», r); }, []), j.useEffect(() => { const r = e.current; if (!r) return; const i = 12, s = ia(t.width / i) * 1.6, o = ia(t.height / i), l = { aspect: s / o }, u = setInterval(() => { let c = «»; for (let d = 0; d < o; d++) { for (let f = 0; f < s; f++) c += oT({ x: f, y: d }, { cols: s, rows: o, metrics: l, time: Date.now() }); c += ` `; } r.textContent = c; }, 1e3 / sT.fps); return () => clearInterval(u); }, [t]), a.jsx(«div», { style: { position: «absolute», top: 0, left: 0, width: «100%», height: «100%» }, children: a.jsx(«div», { ref: e, style: { width: «100%», height: «100%», whiteSpace: «pre», overflow: «hidden» } }) }) ); },
Обычно, как рассказал разработчик, он ищет файл .map, чтобы восстановить оригинальный код, но решил попробовать переложить задачу по объяснению кода на GPT-4. Нейросеть смогла объяснить исходник и обратила внимание на отдельные блоки, рассказав об их предназначении.
Ответ нейросети
The provided code is quite complex, but I’ll break it down into a more understandable format, explaining its different parts and their functionalities.
Overview
The code defines several functions and components using JavaScript, specifically in the context of a React application. It deals with generating some dynamic text content, presumably ASCII art or some kind of visual effect using characters from a predefined set. Additionally, it includes utility functions for event handling, performance timing, and HTML content conversion.
Key Components and Functions
-
Character Set Selection:
const { floor: ra, abs: KE, min: QE } = Math; const O5 = [«reactive.network REACTIVE.NETWORK», «$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/|()1{}[]?-_+~<>i!lI;:,^`’. .:â–‘â–’â–“â–ˆ»]; const G7 = Date.now() % 3 ? O5[1] : O5[0]; const V5 = G7.length;
-
This part of the code imports some common Math functions (floor, abs, and min) and defines two sets of characters, stored in O5.
-
Based on the current time (Date.now() % 3), it selects one of these sets (G7) to use later.
-
The length of the selected character set is stored in V5.
-
-
Dynamic Character Selection Function:
function eT(e, t, n, r) { const i = t.time * 8e-5; const s = QE(t.cols, t.rows); const o = t.metrics.aspect * 0.2; const l = { x: ((4 * (e.x — t.cols / 6.25)) / s) * o, y: (5 * (e.y — t.rows / 4)) / s }; const u = ra(KE(YE(l) — i) * V5 + (ra(e.x / 1) % 2) * 2) % V5; return G7[u]; }
-
The eT function generates a character based on the input position (e.x, e.y) and the current time (t.time).
-
It adjusts the position using some aspect ratio calculations and selects a character from G7 based on a combination of the position and time.
-
The purpose of YE function isn’t directly clear from the code, but it seems to be a custom function used in this calculation.
-
-
React Component (tT) for Displaying Dynamic Content:
const tT = () => { const e = j.useRef(null); const [t, n] = j.useState({ height: null, width: null }); j.useEffect(() => { function r() { n({ height: window.innerHeight, width: window.innerWidth }); } if (typeof window < «u») { n({ height: window.innerHeight, width: window.innerWidth }); window.addEventListener(«resize», r); return () => window.removeEventListener(«resize», r); } }, []); j.useEffect(() => { const r = e.current; if (!r) return; const i = 12; const s = ra(t.width / i) * 1.6; const o = ra(t.height / i); const l = { aspect: s / o }; const u = setInterval(() => { let c = «»; for (let d = 0; d < o; d++) { for (let f = 0; f < s; f++) c += eT({ x: f, y: d }, { cols: s, rows: o, metrics: l, time: Date.now() }); c += ` `; } r.textContent = c; }, 1e3 / JE.fps); return () => clearInterval(u); }, [t]); return a.jsx(«div», { style: { position: «absolute», top: 0, left: 0, width: «100%», height: «100%» }, children: a.jsx(«div», { ref: e, style: { width: «100%», height: «100%», whiteSpace: «pre», overflow: «hidden» } }) }); };
-
This is a React component that sets up an interval to periodically update the content of a div element (e.current) with dynamically generated characters.
-
It calculates the number of columns (s) and rows (o) based on the window size and then fills the div with characters generated by the eTfunction.
-
The effect runs at a specified frames per second (JE.fps).
-
После этого разработчик попросил переписать код на TypeScript так, чтобы он был более читаемым.
Код от нейросетиimport React, { useEffect, useRef, useState } from ‘react’; const selectedCharacterSet = «$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/|()1{}[]?-_+~<>i!lI;:,^`’. .:â–‘â–’â–“â–ˆ»; const characterSetLength = selectedCharacterSet.length; const calculateCharacter = ( x: number, y: number, cols: number, rows: number, aspect: number, time: number, ): string => { const timeFactor = time * 0.000_08; const size = Math.min(cols, rows); const aspectRatio = aspect * 0.2; const position = { x: ((4 * (x — cols / 6.25)) / size) * aspectRatio, y: (5 * (y — rows / 4)) / size, }; const index = Math.floor( Math.abs( Math.cos(position.x * position.x — position.y * position.y) — timeFactor, ) * characterSetLength + (Math.floor(x) % 2) * 2, ) % characterSetLength; return selectedCharacterSet[index]; }; type Size = { height: null | number; width: null | number; }; const AsciiArtGenerator: React.FC = () => { const textRef = useRef<HTMLDivElement>(null); const [size, setSize] = useState<Size>({ height: null, width: null }); useEffect(() => { const handleResize = () => { setSize({ height: window.innerHeight, width: window.innerWidth }); }; // Initial size setting handleResize(); window.addEventListener(‘resize’, handleResize); return () => { window.removeEventListener(‘resize’, handleResize); }; }, []); useEffect(() => { const element = textRef.current; if (!element || !size.width || !size.height) return undefined; const cols = Math.floor(size.width / 12) * 1.6; const rows = Math.floor(size.height / 12); const aspectRatio = cols / rows; const intervalId = setInterval(() => { let content = »; for (let y = 0; y < rows; y++) { for (let x = 0; x < cols; x++) { content += calculateCharacter( x, y, cols, rows, aspectRatio, Date.now(), ); } content += ‘n’; } element.textContent = content; }, 1_000 / 60); // 60 fps return () => { clearInterval(intervalId); }; }, [size]); return ( <div ref={textRef} style={{ height: ‘100%’, overflow: ‘hidden’, whiteSpace: ‘pre’, width: ‘100%’, }} /> ); }; export default () => { return <AsciiArtGenerator />; };
Анимация от нейросети
GPT-4 справилась с запросом, а в результате у разработчика был читаемый код на TypeScript и рабочая анимация ASCII-арта. Фигель заметил, что нейросеть не использовала в своей реализации некоторые символы из оригинального кода (░▒▓█). Он посчитал, что языковая модель не до конца поняла задачу, но на форуме ему подсказали, что это ошибка копировании и вставки.
Источник: habr.com