💻 源码
const CHAINS = [
  {
    id: "linear",
    title: "方法一:线性除法",
    tag: "常见直觉",
    steps: [
      "把 48 天看成「均匀增长」。",
      "一半时间 ≈ 一半面积。",
      "计算:48 ÷ 2 = 24。",
    ],
    answer: 24,
    correct: false,
    note: "误把指数增长当作线性增长。",
  },
  {
    id: "reverse",
    title: "方法二:逆向递推",
    tag: "逆向推理",
    steps: [
      "第 48 天面积 = 100%(满池)。",
      "每天翻倍 ⇒ 第 47 天 = 48 天的一半。",
      "所以第 47 天刚好覆盖 50%。",
    ],
    answer: 47,
    correct: true,
    note: "从终态往回推,符合「翻倍」定义。",
  },
  {
    id: "simulate",
    title: "方法三:正向模拟",
    tag: "枚举验证",
    steps: [
      "设 f(n) 为第 n 天覆盖比例,f(n) = 2 × f(n-1)。",
      "已知 f(48) = 1(满池)。",
      "则 f(47) = 1/2,第 47 天刚好一半。",
    ],
    answer: 47,
    correct: true,
    note: "用递推关系验证,与逆向结论一致。",
  },
];

const SINGLE_CHAIN = CHAINS[0];

const runBtn = document.getElementById("run-btn");
const resetBtn = document.getElementById("reset-btn");
const singleMode = document.getElementById("single-mode");
const statusEl = document.getElementById("status");
const chainsEl = document.getElementById("chains");
const votePanel = document.getElementById("vote-panel");
const voteBars = document.getElementById("vote-bars");
const finalAnswer = document.getElementById("final-answer");

let running = false;

function sleep(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

function setStatus(text, tone = "info") {
  statusEl.textContent = text;
  statusEl.style.borderColor =
    tone === "success"
      ? "rgba(52, 211, 153, 0.35)"
      : tone === "warn"
        ? "rgba(251, 191, 36, 0.35)"
        : "rgba(96, 165, 250, 0.18)";
  statusEl.style.background =
    tone === "success"
      ? "rgba(52, 211, 153, 0.08)"
      : tone === "warn"
        ? "rgba(251, 191, 36, 0.08)"
        : "rgba(96, 165, 250, 0.08)";
}

function createChainCard(chain) {
  const card = document.createElement("article");
  card.className = "chain-card";
  card.dataset.id = chain.id;
  card.innerHTML = `
    <div class="chain-head">
      <div class="chain-title">${chain.title}</div>
      <span class="badge pending">等待中</span>
    </div>
    <div class="chain-steps"></div>
  `;
  return card;
}

function resetDemo() {
  chainsEl.innerHTML = "";
  votePanel.classList.add("hidden");
  voteBars.innerHTML = "";
  finalAnswer.innerHTML = "";
  setStatus("点击按钮,观察三条推理链如何产生答案并投票。");
}

async function animateChain(card, chain) {
  const badge = card.querySelector(".badge");
  const stepsWrap = card.querySelector(".chain-steps");
  card.classList.add("visible");

  badge.textContent = "推理中…";
  badge.className = "badge thinking";

  for (const text of chain.steps) {
    const step = document.createElement("div");
    step.className = "step";
    step.textContent = text;
    stepsWrap.appendChild(step);
    await sleep(80);
    step.classList.add("show");
    await sleep(420);
  }

  const answerStep = document.createElement("div");
  answerStep.className = `step answer ${chain.correct ? "correct" : "wrong"} show`;
  answerStep.textContent = `最终答案:第 ${chain.answer} 天`;
  stepsWrap.appendChild(answerStep);

  badge.textContent = chain.correct ? "推理完成 ✓" : "推理完成 ✗";
  badge.className = `badge ${chain.correct ? "correct" : "wrong"}`;

  if (!chain.correct) {
    card.classList.add("wrong");
  }

  await sleep(300);
  return chain.answer;
}

function renderVote(answers, winner) {
  votePanel.classList.remove("hidden");

  const counts = answers.reduce((acc, value) => {
    acc[value] = (acc[value] || 0) + 1;
    return acc;
  }, {});

  const sorted = Object.entries(counts).sort((a, b) => b[1] - a[1]);
  const maxCount = sorted[0][1];

  voteBars.innerHTML = sorted
    .map(([value, count]) => {
      const pct = (count / answers.length) * 100;
      const cls = Number(value) === 47 ? "a47" : "a24";
      return `
        <div class="vote-row">
          <div class="vote-label">${value} 天</div>
          <div class="bar-track">
            <div class="bar-fill ${cls}" style="width: ${pct}%"></div>
          </div>
          <div class="vote-count">${count} 票</div>
        </div>
      `;
    })
    .join("");

  CHAINS.forEach((chain) => {
    const card = chainsEl.querySelector(`[data-id="${chain.id}"]`);
    if (card && chain.answer === winner) {
      card.classList.add("winner");
    }
  });

  finalAnswer.innerHTML = `
    <div>出现次数最多的答案:<strong>第 ${winner} 天</strong>(${maxCount}/${answers.length} 票)</div>
    <div style="margin-top:8px;color:#cbd5e1;">
      ${winner === 47
        ? "多数表决成功纠正了「24 天」的线性直觉错误。这就是 Self-Consistency 的价值。"
        : "仅单条链运行时没有投票纠错,容易停留在直觉错误上。"}
    </div>
  `;
}

async function runDemo() {
  if (running) return;
  running = true;
  runBtn.disabled = true;
  resetDemo();

  const useSingle = singleMode.checked;
  const chains = useSingle ? [SINGLE_CHAIN] : CHAINS;

  if (useSingle) {
    setStatus("单链模式:只运行「线性除法」这一条推理链……", "warn");
  } else {
    setStatus("正在并行启动 3 条不同推理链……");
  }

  chains.forEach((chain) => {
    chainsEl.appendChild(createChainCard(chain));
  });

  await sleep(350);

  const answers = [];
  for (const chain of chains) {
    const card = chainsEl.querySelector(`[data-id="${chain.id}"]`);
    const answer = await animateChain(card, chain);
    answers.push(answer);
  }

  if (useSingle) {
    setStatus(`单链输出:第 ${answers[0]} 天。没有多数表决,无法纠正潜在错误。`, "warn");
    finalAnswer.innerHTML = `
      <div>单链最终答案:<strong style="color:#fca5a5;">第 ${answers[0]} 天</strong>(错误)</div>
      <div style="margin-top:8px;color:#cbd5e1;">取消勾选「对比模式」,运行 3 条链并观察多数表决如何得到 47 天。</div>
    `;
    votePanel.classList.remove("hidden");
    voteBars.innerHTML = `
      <div class="vote-row">
        <div class="vote-label">${answers[0]} 天</div>
        <div class="bar-track"><div class="bar-fill a24" style="width:100%"></div></div>
        <div class="vote-count">1 票</div>
      </div>
    `;
  } else {
    setStatus("3 条推理链已完成,正在进行多数表决……", "success");

    const counts = answers.reduce((acc, value) => {
      acc[value] = (acc[value] || 0) + 1;
      return acc;
    }, {});

    const winner = Number(
      Object.entries(counts).sort((a, b) => b[1] - a[1])[0][0]
    );

    await sleep(500);
    renderVote(answers, winner);
    setStatus(`多数表决完成:最终正确答案为第 ${winner} 天。`, "success");
  }

  running = false;
  runBtn.disabled = false;
}

runBtn.addEventListener("click", runDemo);
resetBtn.addEventListener("click", resetDemo);

resetDemo();