Как стать автором
Обновить

Разработчик рассказал об опыте деминификации чужого кода с помощью GPT — нейросеть справилась с задачей

Время на прочтение6 мин
Количество просмотров13K

Разработчик 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

  1. 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 (floorabs, 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.

  2. 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.xe.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.

  3. 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-арта. Фигель заметил, что нейросеть не использовала в своей реализации некоторые символы из оригинального кода (░▒▓█). Он посчитал, что языковая модель не до конца поняла задачу, но на форуме ему подсказали, что это ошибка копировании и вставки.

Теги:
Хабы:
Если эта публикация вас вдохновила и вы хотите поддержать автора — не стесняйтесь нажать на кнопку
Всего голосов 9: ↑9 и ↓0+16
Комментарии27

Другие новости

Истории

Работа

Data Scientist
79 вакансий

Ближайшие события

15 – 16 ноября
IT-конференция Merge Skolkovo
Москва
22 – 24 ноября
Хакатон «AgroCode Hack Genetics'24»
Онлайн
28 ноября
Конференция «TechRec: ITHR CAMPUS»
МоскваОнлайн
25 – 26 апреля
IT-конференция Merge Tatarstan 2025
Казань