199 lines
5.7 KiB
TypeScript
199 lines
5.7 KiB
TypeScript
import { Piece, Position, Color, Difficulty, AIMove } from "../types/chess";
|
|
import { getPieceAt, isValidMove } from "./chessRules";
|
|
import { findBestMove } from "./astarAI";
|
|
|
|
// Deepseek API配置 - 请在这里填写您的API密钥
|
|
const DEEPSEEK_API_KEY: string = "sk-b8e21ac94e624691844269f0e0605cf2"; // Deepseek API密钥
|
|
const PLACEHOLDER_KEY = "your-api-key-here";
|
|
const DEEPSEEK_API_URL = "https://api.deepseek.com/v1/chat/completions";
|
|
const DEEPSEEK_MODEL = "deepseek-chat";
|
|
|
|
// 将棋盘转换为FEN-like字符串表示
|
|
function boardToString(pieces: Piece[]): string {
|
|
const board: string[][] = Array(10)
|
|
.fill(null)
|
|
.map(() => Array(9).fill("."));
|
|
|
|
for (const piece of pieces) {
|
|
if (piece.alive) {
|
|
const char = pieceTypeToChar(piece.type, piece.color);
|
|
board[piece.position.row][piece.position.col] = char;
|
|
}
|
|
}
|
|
|
|
return board.map((row) => row.join("")).join("/");
|
|
}
|
|
|
|
// 棋子类型转换为字符
|
|
function pieceTypeToChar(type: string, color: Color): string {
|
|
const charMap: Record<string, string> = {
|
|
帅: "K",
|
|
将: "k",
|
|
仕: "A",
|
|
士: "a",
|
|
相: "B",
|
|
象: "b",
|
|
马: "N",
|
|
车: "R",
|
|
炮: "C",
|
|
兵: "P",
|
|
卒: "p",
|
|
};
|
|
return charMap[type] || "?";
|
|
}
|
|
|
|
// 获取难度对应的系统提示词
|
|
function getSystemPrompt(difficulty: Difficulty, color: Color): string {
|
|
const colorText = color === "red" ? "红方" : "黑方";
|
|
|
|
const basePrompt = `你是一个中国象棋AI助手。当前你是${colorText}。
|
|
棋盘表示:每行用/分隔,每行9个格子,空位用.表示。
|
|
棋子表示:K=帅 k=将 A=仕 a=士 B=相 b=象 N=马 R=车 C=炮 P=兵 p=卒
|
|
红方用大写字母,黑方用小写字母。
|
|
|
|
你必须以以下JSON格式回复你的走法:
|
|
{
|
|
"from": {"row": 行号(0-9), "col": 列号(0-8)},
|
|
"to": {"row": 行号(0-9), "col": 列号(0-8)},
|
|
"reason": "简要说明你的走法思路"
|
|
}
|
|
|
|
注意:
|
|
1. 只能移动己方棋子
|
|
2. 必须遵守中国象棋规则
|
|
3. 行号0是黑方底线,行号9是红方底线
|
|
4. 列号0是左侧,列号8是右侧`;
|
|
|
|
switch (difficulty) {
|
|
case "容易":
|
|
return `${basePrompt}
|
|
|
|
你是初级水平的棋手。请:
|
|
1. 优先选择简单的进攻或防守走法
|
|
2. 避免复杂的战术组合
|
|
3. 偶尔可以犯一些小错误
|
|
4. 优先考虑吃子和将军`;
|
|
|
|
case "一般":
|
|
return `${basePrompt}
|
|
|
|
你是中级水平的棋手。请:
|
|
1. 选择合理的战术走法
|
|
2. 注意保护重要棋子
|
|
3. 寻找吃子机会
|
|
4. 避免明显的错误`;
|
|
|
|
case "困难":
|
|
return `${basePrompt}
|
|
|
|
你是高级水平的棋手。请:
|
|
1. 选择最优的战术走法
|
|
2. 进行深度计算,寻找最佳着法
|
|
3. 积极进攻,同时注意防守
|
|
4. 利用战术组合获得优势
|
|
5. 优先考虑将死对方的将/帅`;
|
|
|
|
default:
|
|
return basePrompt;
|
|
}
|
|
}
|
|
|
|
// 调用Deepseek API获取走法
|
|
export async function getDeepseekMove(
|
|
pieces: Piece[],
|
|
color: Color,
|
|
difficulty: Difficulty,
|
|
moveHistory: string[]
|
|
): Promise<AIMove | null> {
|
|
// 如果API密钥未配置,回退到A* AI
|
|
if (DEEPSEEK_API_KEY === PLACEHOLDER_KEY || !DEEPSEEK_API_KEY) {
|
|
console.log("Deepseek API密钥未配置,使用A* AI代替");
|
|
return findBestMove(pieces, color, difficulty);
|
|
}
|
|
|
|
const boardStr = boardToString(pieces);
|
|
const systemPrompt = getSystemPrompt(difficulty, color);
|
|
const historyStr = moveHistory.slice(-10).join("\n"); // 最近10步
|
|
|
|
const userPrompt = `当前棋盘状态(${color === "red" ? "红方" : "黑方"}走):
|
|
${boardStr}
|
|
|
|
最近走棋记录:
|
|
${historyStr || "无"}
|
|
|
|
请给出你的走法。`;
|
|
|
|
try {
|
|
const response = await fetch(DEEPSEEK_API_URL, {
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
"Authorization": `Bearer ${DEEPSEEK_API_KEY}`,
|
|
},
|
|
body: JSON.stringify({
|
|
model: DEEPSEEK_MODEL,
|
|
messages: [
|
|
{ role: "system", content: systemPrompt },
|
|
{ role: "user", content: userPrompt },
|
|
],
|
|
temperature: difficulty === "容易" ? 0.8 : difficulty === "一般" ? 0.5 : 0.2,
|
|
max_tokens: 500,
|
|
}),
|
|
});
|
|
|
|
if (!response.ok) {
|
|
throw new Error(`API请求失败: ${response.status}`);
|
|
}
|
|
|
|
const data = await response.json();
|
|
const content = data.choices?.[0]?.message?.content;
|
|
|
|
if (!content) {
|
|
throw new Error("API返回内容为空");
|
|
}
|
|
|
|
// 解析JSON响应
|
|
const jsonMatch = content.match(/\{[\s\S]*\}/);
|
|
if (!jsonMatch) {
|
|
throw new Error("无法解析API响应");
|
|
}
|
|
|
|
const moveData = JSON.parse(jsonMatch[0]);
|
|
const from: Position = moveData.from;
|
|
const to: Position = moveData.to;
|
|
|
|
// 验证走法是否合法
|
|
const validation = isValidMove(pieces, from, to, color);
|
|
if (!validation.valid) {
|
|
console.warn(`Deepseek返回的走法不合法: ${validation.reason},回退到A* AI`);
|
|
return findBestMove(pieces, color, difficulty);
|
|
}
|
|
|
|
return {
|
|
from,
|
|
to,
|
|
};
|
|
} catch (error) {
|
|
console.error("Deepseek API调用失败:", error);
|
|
console.log("回退到A* AI");
|
|
return findBestMove(pieces, color, difficulty);
|
|
}
|
|
}
|
|
|
|
// 检查API密钥是否已配置
|
|
export function isDeepseekConfigured(): boolean {
|
|
return DEEPSEEK_API_KEY !== PLACEHOLDER_KEY && !!DEEPSEEK_API_KEY;
|
|
}
|
|
|
|
// 获取API配置状态信息
|
|
export function getDeepseekStatus(): { configured: boolean; message: string } {
|
|
if (isDeepseekConfigured()) {
|
|
return { configured: true, message: "Deepseek API已配置" };
|
|
} else {
|
|
return {
|
|
configured: false,
|
|
message: "Deepseek API未配置,将使用本地A* AI",
|
|
};
|
|
}
|
|
}
|