25-cxsj-final/frontend/src/components/NQueens.jsx

175 lines
7.4 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React, { useState } from 'react';
import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer } from 'recharts';
import { generateNQueensCase, solveNQueensCase, runNQueensBenchmark } from '../api';
const NQueens = () => {
const [n, setN] = useState(8);
const [currentSolution, setCurrentSolution] = useState(null);
const [solutionCount, setSolutionCount] = useState(null);
const [solveTime, setSolveTime] = useState(null);
const [benchmarkResults, setBenchmarkResults] = useState(null);
const [loading, setLoading] = useState(false);
const handleSolve = async () => {
setLoading(true);
try {
// Generate is trivial (just N), so we skip explicit generate step for UI simplicity
// and just call solve directly
const results = await solveNQueensCase(n, ["backtracking"]);
if (results && results.length > 0) {
const res = results[0];
setCurrentSolution(res.first_solution);
setSolutionCount(res.solution_count);
setSolveTime(res.time_seconds);
}
} catch (error) {
console.error("Error solving N-Queens:", error);
alert("求解失败");
}
setLoading(false);
};
const handleBenchmark = async () => {
setLoading(true);
try {
const sizes = [4, 8, 10, 12, 13];
const results = await runNQueensBenchmark(sizes, ["backtracking"]);
const chartData = results.map(item => {
const point = { size: item.size };
item.algorithms.forEach(algo => {
if (algo.time_seconds !== undefined && algo.time_seconds !== null) {
point[algo.algorithm] = algo.time_seconds;
}
});
return point;
});
setBenchmarkResults(chartData);
} catch (error) {
console.error("Error running benchmark:", error);
alert("性能测试失败");
}
setLoading(false);
};
// Render the chess board
const renderBoard = () => {
if (!currentSolution) return null;
const boardSize = 400;
const cellSize = boardSize / n;
return (
<div
className="relative border-2 border-gray-800"
style={{ width: boardSize, height: boardSize }}
>
{Array.from({ length: n }).map((_, row) => (
Array.from({ length: n }).map((_, col) => {
const isBlack = (row + col) % 2 === 1;
const hasQueen = currentSolution[row] === col;
return (
<div
key={`${row}-${col}`}
className={`absolute flex items-center justify-center ${isBlack ? 'bg-gray-600' : 'bg-white'}`}
style={{
width: cellSize,
height: cellSize,
top: row * cellSize,
left: col * cellSize
}}
>
{hasQueen && <span className="text-red-500 font-bold" style={{ fontSize: cellSize * 0.7 }}></span>}
</div>
);
})
))}
</div>
);
};
return (
<div className="p-6 space-y-8">
<h1 className="text-3xl font-bold text-gray-800">N皇后问题 (N-Queens)</h1>
<p className="text-gray-600">目标 N×N 的棋盘上放置 N 个皇后使得它们互不攻击</p>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
{/* Controls */}
<div className="bg-white p-6 rounded-lg shadow space-y-6">
<div>
<label className="block text-sm font-medium text-gray-700">棋盘大小 (N)</label>
<input
type="number"
min="4"
max="14"
value={n}
onChange={(e) => setN(parseInt(e.target.value))}
className="mt-1 block w-full border rounded-md p-2"
/>
<p className="text-xs text-gray-500 mt-1">建议 N &le; 14否则计算时间过长</p>
</div>
<button
onClick={handleSolve}
disabled={loading}
className="w-full bg-blue-600 text-white py-2 px-4 rounded-md hover:bg-blue-700 disabled:bg-gray-400"
>
{loading ? '计算中...' : '求解 (Backtracking)'}
</button>
{solutionCount !== null && (
<div className="mt-4 p-4 bg-green-50 rounded-md">
<p className="font-medium text-green-800">找到解的数量: {solutionCount}</p>
<p className="text-sm text-green-600">耗时: {solveTime?.toFixed(6)} </p>
</div>
)}
</div>
{/* Visualization */}
<div className="bg-white p-6 rounded-lg shadow flex justify-center items-center min-h-[450px]">
{currentSolution ? renderBoard() : (
<div className="text-gray-400 text-center">
<p>点击求解以查看结果</p>
</div>
)}
</div>
</div>
{/* Benchmark */}
<div className="bg-white p-6 rounded-lg shadow">
<div className="flex justify-between items-center mb-6">
<h2 className="text-xl font-semibold">性能测试 (N=4 to 13)</h2>
<button
onClick={handleBenchmark}
disabled={loading}
className="bg-purple-600 text-white py-2 px-4 rounded-md hover:bg-purple-700 disabled:bg-gray-400"
>
运行性能测试
</button>
</div>
{benchmarkResults && (
<div className="h-96 w-full">
<ResponsiveContainer width="100%" height="100%">
<LineChart
data={benchmarkResults}
margin={{ top: 5, right: 30, left: 20, bottom: 5 }}
>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="size" label={{ value: 'N', position: 'insideBottomRight', offset: -10 }} />
<YAxis label={{ value: '耗时 (秒)', angle: -90, position: 'insideLeft' }} />
<Tooltip />
<Legend />
<Line type="monotone" dataKey="backtracking" stroke="#ff0000" name="回溯算法" />
</LineChart>
</ResponsiveContainer>
</div>
)}
</div>
</div>
);
};
export default NQueens;