"use client"; import React, { useEffect, useRef, useState } from "react"; import Coin from "@/components/coin"; import Hexagram, { HexagramObj } from "@/components/hexagram"; import { bool } from "aimless.js"; import Result, { ResultObj } from "@/components/result"; import Question from "@/components/question"; import ResultAI from "@/components/result-ai"; import { animateChildren } from "@/lib/animate"; import guaIndexData from "@/lib/data/gua-index.json"; import guaListData from "@/lib/data/gua-list.json"; import { getAnswer } from "@/app/server"; import { readStreamableValue } from "ai/rsc"; import { Button } from "./ui/button"; import { BrainCircuit, ListRestart } from "lucide-react"; import { ERROR_PREFIX } from "@/lib/constant"; const AUTO_DELAY = 600; function Divination() { const [error, setError] = useState(""); const [isLoading, setIsLoading] = useState(false); const [completion, setCompletion] = useState(""); async function onCompletion() { setError(""); setCompletion(""); setIsLoading(true); try { const { data, error } = await getAnswer( question, resultObj!.guaMark, resultObj!.guaTitle, resultObj!.guaResult, resultObj!.guaChange, ); if (error) { setError(error); return; } if (data) { let ret = ""; for await (const delta of readStreamableValue(data)) { if (delta.startsWith(ERROR_PREFIX)) { setError(delta.slice(ERROR_PREFIX.length)); return; } ret += delta; setCompletion(ret); } } } catch (err: any) { setError(err.message ?? err); } finally { setIsLoading(false); } } const [frontList, setFrontList] = useState([true, true, true]); const [rotation, setRotation] = useState(false); const [hexagramList, setHexagramList] = useState([]); const [resultObj, setResultObj] = useState(null); const [question, setQuestion] = useState(""); const [resultAi, setResultAi] = useState(false); const flexRef = useRef(null); const [count, setCount] = useState(0); // 自动卜筮 useEffect(() => { if (rotation || resultObj || count >= 6 || !question) { return; } setTimeout(startClick, AUTO_DELAY); }, [question, rotation]); useEffect(() => { if (!flexRef.current) { return; } const observer = animateChildren(flexRef.current); return () => observer.disconnect(); }, []); function onTransitionEnd() { setRotation(false); let frontCount = frontList.reduce((acc, val) => (val ? acc + 1 : acc), 0); setHexagramList((list) => { const newList = [ ...list, { change: frontCount == 0 || frontCount == 3 || null, yang: frontCount >= 2, separate: list.length == 3, }, ]; setResult(newList); return newList; }); } function startClick() { if (rotation) { return; } if (hexagramList.length >= 6) { setHexagramList([]); } setFrontList([bool(), bool(), bool()]); setRotation(true); setCount(count + 1); } async function testClick() { for (let i = 0; i < 6; i++) { onTransitionEnd(); } } function restartClick() { setResultObj(null); setHexagramList([]); setQuestion(""); setResultAi(false); setCount(0); stop(); } function aiClick() { setResultAi(true); onCompletion(); } function setResult(list: HexagramObj[]) { if (list.length != 6) { return; } const guaDict1 = ["坤", "震", "坎", "兑", "艮", "离", "巽", "乾"]; const guaDict2 = ["地", "雷", "水", "泽", "山", "火", "风", "天"]; const changeYang = ["初九", "九二", "九三", "九四", "九五", "上九"]; const changeYin = ["初六", "六二", "六三", "六四", "六五", "上六"]; const changeList: String[] = []; list.forEach((value, index) => { if (!value.change) { return; } changeList.push(value.yang ? changeYang[index] : changeYin[index]); }); // 卦的结果: 第X卦 X卦 XX卦 X上X下 // 计算卦的索引,111对应乾卦,000对应坤卦,索引转为10进制。 const upIndex = (list[5].yang ? 4 : 0) + (list[4].yang ? 2 : 0) + (list[3].yang ? 1 : 0); const downIndex = (list[2].yang ? 4 : 0) + (list[1].yang ? 2 : 0) + (list[0].yang ? 1 : 0); const guaIndex = guaIndexData[upIndex][downIndex] - 1; const guaName1 = guaListData[guaIndex]; let guaName2; if (upIndex === downIndex) { // 上下卦相同,格式为X为X guaName2 = guaDict1[upIndex] + "为" + guaDict2[upIndex]; } else { guaName2 = guaDict2[upIndex] + guaDict2[downIndex] + guaName1; } const guaDesc = guaDict1[upIndex] + "上" + guaDict1[downIndex] + "下"; setResultObj({ // 例:26.山天大畜 guaMark: `${(guaIndex + 1).toString().padStart(2, "0")}.${guaName2}`, guaTitle: `周易第${guaIndex + 1}卦`, // 例:大畜卦(山天大畜)_艮上乾下 guaResult: `${guaName1}卦(${guaName2})_${guaDesc}`, guaChange: changeList.length === 0 ? "无变爻" : `变爻: ${changeList.toString()}`, }); } const showResult = resultObj !== null; const inputQuestion = question === ""; return (
{!resultAi && !inputQuestion && ( )} {!inputQuestion && !showResult && (
🎲 第{" "} {count === 0 ? "-/-" : `${count}/6`} {" "} 次卜筮
)} {!inputQuestion && hexagramList.length != 0 && (
{showResult && (
{resultAi ? null : ( )}
)}
)} {resultAi && ( )}
); } export default Divination;