게시판SGF 기보로 학습하는 바둑 LLM 인공지능예제 소스

예제 소스

SGF 기보로 학습하는 바둑 LLM 인공지능 · 파일 169개

AtariEffect.tsx

"use client";

import { useEffect, useRef, useState } from "react";
import styles from "./AtariEffect.module.css";

const ANIM_SRC = "/effects/guanyu_danshu.svg";
const SOUND_SRC = "/sounds/danshu.wav";
const SHOW_MS = 1900;

export type AtariTrigger = { id: number; who: 1 | 2 };

/** 단수 연출: 관우 청룡언월도 애니메이션 + "단수~" 소리. trigger.id 가 바뀔 때마다 처음부터 재생. */
export function AtariEffect({ trigger }: { trigger: AtariTrigger | null }) {
  const [src, setSrc] = useState<string | null>(null);
  const [visible, setVisible] = useState(false);
  const blobRef = useRef<Blob | null>(null);
  const urlRef = useRef<string | null>(null);
  const audioRef = useRef<HTMLAudioElement | null>(null);

  useEffect(() => {
    let cancelled = false;
    fetch(ANIM_SRC)
      .then((res) => (res.ok ? res.blob() : null))
      .then((b) => {
        if (!cancelled && b) blobRef.current = b;
      })
      .catch(() => undefined);
    const audio = new Audio(SOUND_SRC);
    audio.preload = "auto";
    audio.volume = 0.8;
    audioRef.current = audio;
    return () => {
      cancelled = true;
      if (urlRef.current) URL.revokeObjectURL(urlRef.current);
    };
  }, []);

  useEffect(() => {
    if (!trigger) return;
    // 애니메이션 이미지는 URL이 같으면 이어서 재생되므로 매번 새 object URL로 처음부터 돌린다.
    if (urlRef.current) URL.revokeObjectURL(urlRef.current);
    urlRef.current = null;
    if (blobRef.current) {
      urlRef.current = URL.createObjectURL(blobRef.current);
      setSrc(urlRef.current);
    } else {
      setSrc(`${ANIM_SRC}?t=${trigger.id}`);
    }
    setVisible(true);

    const audio = audioRef.current;
    if (audio) {
      audio.currentTime = 0;
      audio.play().catch(() => undefined);
    }
    const timer = window.setTimeout(() => setVisible(false), SHOW_MS);
    return () => window.clearTimeout(timer);
  }, [trigger]);

  if (!visible || !src || !trigger) return null;
  return (
    <div className={styles.layer} aria-live="polite" key={trigger.id}>
      {/* eslint-disable-next-line @next/next/no-img-element */}
      <img className={styles.anim} src={src} alt="" />
      <div className={`${styles.word} ${trigger.who === 1 ? styles.black : styles.white}`}>
        단수!
      </div>
    </div>
  );
}