基於 LLM + Next.js 的博弈實戰猜拳遊戲
摘要:當你以為自己在和隨機數生成器玩遊戲時,對面的 AI 正在閲讀你所有的歷史出拳記錄,並寫了一篇關於你心理狀態的小作文。本文帶你拆解這個基於 Next.js 16 + Tailwind v4 + LLM 的“過度設計”+“沒啥技術含量”+”有那麼點意思“項目。
👉 先給老闆們體驗:(具體規則有手就能玩!)
優先地址:https://rps.anhejin.cn
有條件的:https://rps-eta-ten.vercel.app

最近閒着沒事,突然想這用AI寫點代碼。作為一名要把“簡單需求複雜化”刻在 DNA 裏的老前端,我決定拿最簡單的“剪刀石頭布”開刀。
在這個 AI 滿天飛的時代,還在用 Math.random() 寫對手邏輯未免太沒追求了。於是,我基於最新的技術棧(Next.js 16 + React 19),接入了 OpenAI等一眾大模型,搞出了一個會“讀心”、會嘲諷,甚至懂博弈論的猜拳遊戲。
為什麼是剪刀石頭布?
別笑,剪刀石頭布其實是一個極佳的博弈論模型。
- 新手:完全隨機(Chaos)。
- 普通人:贏了保持,輸了變招(Win-Stay, Lose-Shift)。
- 高手:預判你的預判。
我的目標是:構建一個能看穿你心理的 AI,並且用目前最前沿的前端技術棧把它跑起來。
技術選型:這就叫“殺雞用牛刀”
為了配得上這個“高智商”AI,我在技術棧上直接拉滿,全部採用了目前(2025-2026)的最新穩定版:
- Next.js 16.1 (App Router):服務端組件(RSC)處理核心邏輯,隱藏 AI 的 Prompt,保證你沒法通過 F12 偷看答案。
- React 19:享受最新的 Hooks 和併發特性。
- Tailwind CSS v4:對,就是那個不用配置構建工具、性能起飛的 v4 版本。關鍵是AI喜歡用這個
- SQLite + LibSQL:輕量級數據庫,用來記仇——啊不,記錄你的勝負數據。
核心玩法:AI 是怎麼“讀心”的?
這個項目的核心不在於 UI 有多炫(雖然 Tailwind 4 確實很潤),而在於 /lib/ai-service.ts 裏的那段邏輯。
傳統的遊戲 AI 往往是預設好的 if-else。但在我的設計裏,每一輪遊戲,我都會把你在這個 Session 裏的所有歷史記錄打包,像講故事一樣發給 LLM(大語言模型):
// lib/ai-service.ts
// 構建遊戲歷史描述
const historyDescription = history.length > 0
? history.map((h) =>
`第${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
],
// ...
});
System: 你是一個猜拳高手,你的對手是一個普通人類。
User: 前幾輪戰況如下:第1輪玩家出剪刀,你出布(輸);第2輪玩家出石頭,你出布(贏)。現在是第3輪,請分析玩家的心理,並給出你的出拳。
不僅如此,我給 AI 設定了兩種模式:
- 策略模式(Strategy):降低模型的
temperature(隨機性),讓它進行嚴密的邏輯推理。比如它會分析:“玩家上一把輸了,根據心理學,這把他大概率會出剋制我上一把的招數,所以我預判他……” - 混沌模式(Chaos):拉高
temperature,讓 AI 徹底放飛自我,主打一個亂拳打死老師傅。
優雅降級:當 GPT 腦幹缺失時
作為老全棧,必須要考慮一種情況:如果 API 掛了,或者響應超時了怎麼辦?
難道讓用户乾等着轉圈圈?絕對不行。
我在後端實現了一套基於傳統統計學的本地算法作為“備胎”。如果 LLM 在規定時間內沒有響應,系統會無縫切換到本地邏輯。這個本地邏輯一點也不弱,它內置了經典的策略庫(代碼在 lib/game.ts):
// lib/game.ts 裏的心理學博弈邏輯
// 1. 如果玩家上輪輸了,傾向於出能剋制AI上一招的選項 (Win-Stay, Lose-Shift的變種)
if (lastResult === 'ai_win') {
const lastAIChoice = lastRound.ai_choice as Choice;
// 預測玩家會出剋制我不上一把的牌
const predictedPlayerChoice = whatBeatsAI[lastAIChoice];
// 那我就預判你的預判
return counterMoves[predictedPlayerChoice];
}
// 2. 如果玩家上輪贏了,可能繼續用同一招
if (lastResult === 'player_win') {
// 玩家可能繼續用同一招,直接剋制它
return counterMoves[lastPlayerChoice];
}
這些策略包括:
- 頻率分析:如果你一直出石頭,它就會瘋狂出布。
- 反制連勝:如果你贏了,它會假設你會繼續出一樣的,直接剋制你。
在代碼實現上,這只是一個簡單的 try-catch 降級,但對用户體驗來説是質的飛躍。用户根本感覺不到 AI 掉線了,只會感覺“這傢伙怎麼變風格了?”
全棧體驗:Next.js App Router 的絲滑
在 Next.js 16 中,前後端的邊界變得非常模糊(褒義)。本項目使用了 App Router 的 Route Handlers 來處理遊戲邏輯。
前端組件調用後端接口就像調用本地函數一樣自然:
// src/app/game/[id]/page.tsx
const playRound = useCallback(async (choice: Choice | null, timeout: boolean = false) => {
// ...
const res = await fetch('/api/game/play', {
method: 'POST',
body: JSON.stringify({ /*...*/ }),
});
// ...
}, []);
而在服務端 (src/app/api/game/play/route.ts),我們完成了完整的業務閉環:
// 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. 判定勝負 & 寫入數據庫
const result = determineWinner(playerChoice, aiChoiceResult.choice);
// ...
return NextResponse.json({ /*...*/ });
}
這一套流程行雲流水,類型安全雖然不如 Server Actions 極致,但通過共享類型定義(Shared Types),依然能保證前後端的一致性。不用寫繁瑣的 Swagger,不用搞複雜的 Redux,一把梭。
實際上手:由於太會嘲諷導致不想玩了
為了增加趣味性,我讓 AI 不僅輸出“石頭/剪刀/布”,還要輸出一段 Reasoning(推理過程) 和 Comment(賽後嘲諷)。
當你輸掉比賽時,你可能會看到這樣的結算語:
“我看你第一把猶豫了很久出了剪刀,我就知道你是個保守的人。下一把別這麼明顯了,人類。”
説實話,代碼寫完後我自己測試了幾把,勝率居然只有 40% 左右。看着屏幕上 AI 的嘲諷,我即使作為開發者也不禁懷疑:這玩意兒是不是真有意識?
體驗地址
雖説代碼沒什麼核心科技,但帶來的博弈體驗確實很有趣。我已經把項目部署上去了,歡迎來挑戰(或者被虐):
👉 在線體驗
優先地址:https://rps.anhejin.cn
有條件的:https://rps-eta-ten.vercel.app
總結
這個項目證明了一件事:技術是冰冷的,但通過簡單的創意組合,可以創造出有温度(甚至有點燙手)的交互體驗。 Next.js 16 和 React 19 的組合讓全棧開發的門檻進一步降低,讓我們有更多精力去關注“玩法”本身,而不是被構建配置折磨。
如果你對源碼感興趣,或者想改改 Prompt 把 AI 調教成“討好型人格”,歡迎去 GitHub 扒代碼。
注:
- 本文僅供技術交流,玩遊戲輸給 AI 請勿用拳頭擊打顯示器,開發者概不負責。
- 這只是一個娛樂小遊戲,結果具有隨機性,不代表任何AI大模型的真實能力。
- 遊戲結果僅供娛樂,不應用於評估或比較AI模型的實際性能。
- 網站不收集、存儲或分享任何個人信息或用户數據。