卢泓冶 發表於 2026-1-22 08:29:00

绝望博弈!一众大模型加持的猜拳游戏,人类胜率竟不足10%?

<h1>基于 LLM + Next.js 的博弈实战猜拳游戏</h1>
<p><strong>摘要</strong>:当你以为自己在和随机数生成器玩游戏时,对面的 AI 正在阅读你所有的历史出拳记录,并写了一篇关于你心理状态的小作文。本文带你拆解这个基于 Next.js 16 + Tailwind v4 + LLM 的“过度设计”+“没啥技术含量”+”有那么点意思“项目。</p>
<p>👉 <strong>先给老板们体验:(具体规则有手就能玩!)</strong></p>
<p>优先地址:https://rps.anhejin.cn</p>
<p>有条件的:https://rps-eta-ten.vercel.app<br></p>
<p><img src="https://img2024.cnblogs.com/blog/809672/202601/809672-20260121154520537-280291882.png"></p>
<hr>
<p>最近闲着没事,突然想这用AI写点代码。作为一名要把“简单需求复杂化”刻在 DNA 里的老前端,我决定拿最简单的“剪刀石头布”开刀。</p>
<p>在这个 AI 满天飞的时代,还在用 <code>Math.random()</code> 写对手逻辑未免太没追求了。于是,我基于最新的技术栈(Next.js 16 + React 19),接入了 OpenAI等一众大模型,搞出了一个会“读心”、会嘲讽,甚至懂博弈论的猜拳游戏。</p>
<h2>为什么是剪刀石头布?</h2>
<p>别笑,剪刀石头布其实是一个极佳的博弈论模型。</p>
<ul>
<li><strong>新手</strong>:完全随机(Chaos)。</li>
<li><strong>普通人</strong>:赢了保持,输了变招(Win-Stay, Lose-Shift)。</li>
<li><strong>高手</strong>:预判你的预判。</li>










</ul>
<p>我的目标是:<strong>构建一个能看穿你心理的 AI,并且用目前最前沿的前端技术栈把它跑起来。</strong></p>
<h2>技术选型:这就叫“杀鸡用牛刀”</h2>
<p>为了配得上这个“高智商”AI,我在技术栈上直接拉满,全部采用了目前(2025-2026)的最新稳定版:</p>
<ol>
<li><strong>Next.js 16.1 (App Router)</strong>:服务端组件(RSC)处理核心逻辑,隐藏 AI 的 Prompt,保证你没法通过 F12 偷看答案。</li>
<li><strong>React 19</strong>:享受最新的 Hooks 和并发特性。</li>
<li><strong>Tailwind CSS v4</strong>:对,就是那个不用配置构建工具、性能起飞的 v4 版本。关键是AI喜欢用这个</li>
<li><strong>SQLite + LibSQL</strong>:轻量级数据库,用来记仇——啊不,记录你的胜负数据。</li>










</ol>
<h2>核心玩法:AI 是怎么“读心”的?</h2>
<p>这个项目的核心不在于 UI 有多炫(虽然 Tailwind 4 确实很润),而在于 <code>/lib/ai-service.ts</code> 里的那段逻辑。</p>
<p>传统的游戏 AI 往往是预设好的 <code>if-else</code>。但在我的设计里,每一轮游戏,我都会把你在这个 Session 里的所有历史记录打包,像讲故事一样发给 LLM(大语言模型):</p>
<pre><code class="language-typescript">
// lib/ai-service.ts

// 构建游戏历史描述
const historyDescription = history.length &gt; 0
? history.map((h) =&gt;
      `第${h.round}轮: 玩家出${translateChoice(h.player_choice)},
       AI出${translateChoice(h.ai_choice)},
       结果: ${translateResult(h.result)}`
    ).join("\n")
: "这是第一轮,没有历史记录。";

// ...发送给 LLM
const response = await client.chat.completions.create({
messages: [
    { role: "system", content: systemPrompt },
    { role: "user", content: userPrompt }, // 这里包含了 historyDescription
],
// ...
});
</code></pre>
<blockquote>
<p>System: 你是一个猜拳高手,你的对手是一个普通人类。<br>
User: 前几轮战况如下:第1轮玩家出剪刀,你出布(输);第2轮玩家出石头,你出布(赢)。现在是第3轮,请分析玩家的心理,并给出你的出拳。</p>












</blockquote>
<p><strong>不仅如此,我给 AI 设定了两种模式:</strong></p>
<ol>
<li><strong>策略模式(Strategy)</strong>:降低模型的 <code>temperature</code>(随机性),让它进行严密的逻辑推理。比如它会分析:“玩家上一把输了,根据心理学,这把他大概率会出克制我上一把的招数,所以我预判他……”</li>
<li><strong>混沌模式(Chaos)</strong>:拉高 <code>temperature</code>,让 AI 彻底放飞自我,主打一个乱拳打死老师傅。</li>












</ol>
<h2>优雅降级:当 GPT 脑干缺失时</h2>
<p>作为老全栈,必须要考虑一种情况:<strong>如果 API 挂了,或者响应超时了怎么办?</strong></p>
<p>难道让用户干等着转圈圈?绝对不行。</p>
<p>我在后端实现了一套基于传统统计学的<strong>本地算法</strong>作为“备胎”。如果 LLM 在规定时间内没有响应,系统会无缝切换到本地逻辑。这个本地逻辑一点也不弱,它内置了经典的策略库(代码在 <code>lib/game.ts</code>):</p>
<pre><code class="language-typescript">
// lib/game.ts 里的心理学博弈逻辑

// 1. 如果玩家上轮输了,倾向于出能克制AI上一招的选项 (Win-Stay, Lose-Shift的变种)
if (lastResult === 'ai_win') {
const lastAIChoice = lastRound.ai_choice as Choice;
// 预测玩家会出克制我不上一把的牌
const predictedPlayerChoice = whatBeatsAI;
// 那我就预判你的预判
return counterMoves;
}

// 2. 如果玩家上轮赢了,可能继续用同一招
if (lastResult === 'player_win') {
// 玩家可能继续用同一招,直接克制它
return counterMoves;
}
</code></pre>
<p>这些策略包括:</p>
<ul>
<li><strong>频率分析</strong>:如果你一直出石头,它就会疯狂出布。</li>
<li><strong>反制连胜</strong>:如果你赢了,它会假设你会继续出一样的,直接克制你。</li>
</ul>
<p>在代码实现上,这只是一个简单的 <code>try-catch</code> 降级,但对用户体验来说是质的飞跃。用户根本感觉不到 AI 掉线了,只会感觉“这家伙怎么变风格了?”</p>
<h2>全栈体验:Next.js App Router 的丝滑</h2>
<p>在 Next.js 16 中,前后端的边界变得非常模糊(褒义)。本项目使用了 App Router 的 Route Handlers 来处理游戏逻辑。</p>
<p>前端组件调用后端接口就像调用本地函数一样自然:</p>
<pre><code class="language-typescript">
// src/app/game//page.tsx
const playRound = useCallback(async (choice: Choice | null, timeout: boolean = false) =&gt; {
    // ...
    const res = await fetch('/api/game/play', {
      method: 'POST',
      body: JSON.stringify({ /*...*/ }),
    });
    // ...
}, []);
</code></pre>
<p>而在服务端 (<code>src/app/api/game/play/route.ts</code>),我们完成了完整的业务闭环:</p>
<pre><code class="language-typescript">
// src/app/api/game/play/route.ts
export async function POST(request: NextRequest) {
    // 1. 身份校验与数据库读取
    const { sessionId, playerChoice } = await request.json();
    const baseSession = await db.execute(/*...*/);

    // 2. 调用 AI (带超时降级)
    // 如果 API 响应太慢,这里会自动切换到本地逻辑
    const aiChoiceResult = await Promise.race([
      getAIChoiceFromAPI(aiConfig, history, difficulty),
      timeoutPromise // 设定的超时时间
    ]);

    // 3. 判定胜负 &amp; 写入数据库
    const result = determineWinner(playerChoice, aiChoiceResult.choice);
    // ...
   
    return NextResponse.json({ /*...*/ });
}
</code></pre>
<p>这一套流程行云流水,类型安全虽然不如 Server Actions 极致,但通过共享类型定义(Shared Types),依然能保证前后端的一致性。不用写繁琐的 Swagger,不用搞复杂的 Redux,一把梭。</p>
<h2>实际上手:由于太会嘲讽导致不想玩了</h2>
<p>为了增加趣味性,我让 AI 不仅输出“石头/剪刀/布”,还要输出一段 <strong>Reasoning(推理过程)</strong> 和 <strong>Comment(赛后嘲讽)</strong>。</p>
<p>当你输掉比赛时,你可能会看到这样的结算语:<br>
<em>“我看你第一把犹豫了很久出了剪刀,我就知道你是个保守的人。下一把别这么明显了,人类。”</em></p>
<p>说实话,代码写完后我自己测试了几把,胜率居然只有 40% 左右。看着屏幕上 AI 的嘲讽,我即使作为开发者也不禁怀疑:这玩意儿是不是真有意识?</p>
<h2>体验地址</h2>
<p>虽说代码没什么核心科技,但带来的博弈体验确实很有趣。我已经把项目部署上去了,欢迎来挑战(或者被虐):</p>
<p>👉 <strong>在线体验</strong></p>
<p>优先地址:https://rps.anhejin.cn</p>
<p>有条件的:https://rps-eta-ten.vercel.app<br></p>
<h2>总结</h2>
<p>这个项目证明了一件事:<strong>技术是冰冷的,但通过简单的创意组合,可以创造出有温度(甚至有点烫手)的交互体验。</strong> Next.js 16 和 React 19 的组合让全栈开发的门槛进一步降低,让我们有更多精力去关注“玩法”本身,而不是被构建配置折磨。</p>
<p>如果你对源码感兴趣,或者想改改 Prompt 把 AI 调教成“讨好型人格”,欢迎去 GitHub 扒代码。</p>
<hr>
<p><em>注:</em></p>
<ol>
<li>本文仅供技术交流,玩游戏输给 AI 请勿用拳头击打显示器,开发者概不负责。</li>
<li>这只是一个娱乐小游戏,结果具有随机性,不代表任何AI大模型的真实能力。</li>
<li>游戏结果仅供娱乐,不应用于评估或比较AI模型的实际性能。</li>
<li>网站不收集、存储或分享任何个人信息或用户数据。</li>












</ol><br><br>
来源:https://www.cnblogs.com/greywen/p/19512202
頁: [1]
查看完整版本: 绝望博弈!一众大模型加持的猜拳游戏,人类胜率竟不足10%?