Zebra / frontend /src /pages /PredefinedPuzzlePage.js
guoj5's picture
added custom puzzle page
ce10613
import React, { useState, useEffect } from 'react';
function PredefinedPuzzlePage() {
// For puzzle index and puzzle data
const [puzzleIndex, setPuzzleIndex] = useState(0);
const [puzzleText, setPuzzleText] = useState("");
const [expectedSolution, setExpectedSolution] = useState(null);
// sysContent can be editted, default using Example.txt
const [sysContent, setSysContent] = useState("");
// Interaction results
const [generatedCode, setGeneratedCode] = useState("");
const [executionSuccess, setExecutionSuccess] = useState(null);
const [attempts, setAttempts] = useState(0);
const [isSolving, setIsSolving] = useState(false);
const [problematicConstraints, setProblematicConstraints] = useState("");
// UI state
const [currentStep, setCurrentStep] = useState(1);
const [expandedSections, setExpandedSections] = useState({
puzzle: true,
sysContent: false,
result: false
});
// Frontend fetch sysContent in default
useEffect(() => {
fetch(`/default_sys_content`)
.then(res => res.json())
.then(data => {
if(data.success) {
setSysContent(data.sysContent);
}
})
.catch(e => console.error(e));
}, []);
// When puzzleIndex changing,auto get puzzle
useEffect(() => {
fetch(`/get_puzzle?index=${puzzleIndex}`)
.then(res => res.json())
.then(data => {
if(data.success) {
setPuzzleText(data.puzzle);
setExpectedSolution(data.expected_solution);
} else {
console.error("Failed to fetch puzzle", data.error);
setPuzzleText("");
setExpectedSolution(null);
}
})
.catch(e => console.error(e));
}, [puzzleIndex]);
const handleSolve = () => {
if(!puzzleText || !expectedSolution) {
alert("puzzle or expectedSolution incomplete");
return;
}
const payload = {
index: puzzleIndex,
puzzle: puzzleText,
expected_solution: expectedSolution,
sys_content: sysContent,
problematic_constraints: problematicConstraints
};
setIsSolving(true);
setCurrentStep(3);
setExpandedSections({...expandedSections, result: true});
fetch(`/solve`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload)
})
.then(res => res.json())
.then(data => {
if(!data.success) {
alert("Backend error: " + data.error);
return;
}
const result = data.result;
setGeneratedCode(result.generatedCode || "");
setExecutionSuccess(result.success);
setAttempts(result.attempts || 0);
setProblematicConstraints(result.problematicConstraints || "");
})
.catch(e => console.error(e))
.finally(() => {
setIsSolving(false);
});
};
const toggleSection = (section) => {
setExpandedSections({
...expandedSections,
[section]: !expandedSections[section]
});
};
const nextStep = () => {
if (currentStep < 3) {
setCurrentStep(currentStep + 1);
if (currentStep === 1) {
setExpandedSections({puzzle: false, sysContent: true, result: false});
} else if (currentStep === 2) {
setExpandedSections({puzzle: false, sysContent: false, result: true});
}
}
};
const prevStep = () => {
if (currentStep > 1) {
setCurrentStep(currentStep - 1);
if (currentStep === 2) {
setExpandedSections({puzzle: true, sysContent: false, result: false});
} else if (currentStep === 3) {
setExpandedSections({puzzle: false, sysContent: true, result: false});
}
}
};
return (
<>
{/* Progress Steps */}
<div className="progress-container">
<div className="progress-steps">
<div className={`step ${currentStep >= 1 ? 'active' : ''}`}>
<div className="step-number">1</div>
<div className="step-label">Select Puzzle</div>
</div>
<div className={`step ${currentStep >= 2 ? 'active' : ''}`}>
<div className="step-number">2</div>
<div className="step-label">Configure System</div>
</div>
<div className={`step ${currentStep >= 3 ? 'active' : ''}`}>
<div className="step-number">3</div>
<div className="step-label">Solve & Results</div>
</div>
</div>
</div>
<main className="main-content">
{/* Step 1: Puzzle Selection */}
<section className={`content-section ${expandedSections.puzzle ? 'expanded' : 'collapsed'}`}>
<div className="section-header" onClick={() => toggleSection('puzzle')}>
<h2>📋 Puzzle Selection</h2>
<span className="toggle-icon">{expandedSections.puzzle ? '▼' : '▶'}</span>
</div>
{expandedSections.puzzle && (
<div className="section-content">
<div className="puzzle-selector">
<label htmlFor="puzzle-index">Choose puzzle index (0 - 999):</label>
<div className="input-group">
<input
id="puzzle-index"
type="number"
value={puzzleIndex}
onChange={(e) => setPuzzleIndex(Number(e.target.value))}
min={0}
max={999}
className="number-input"
/>
<button
onClick={() => setPuzzleIndex(puzzleIndex)}
className="btn btn-secondary"
>
Load Puzzle
</button>
</div>
</div>
<div className="puzzle-display">
<div className="puzzle-text">
<h3>📄 Puzzle Text</h3>
<div className="text-display">
{puzzleText || "Loading puzzle..."}
</div>
</div>
<div className="expected-solution">
<h3>🎯 Expected Solution</h3>
<div className="json-display">
{expectedSolution ? (
<pre>{JSON.stringify(expectedSolution, null, 2)}</pre>
) : (
"Loading solution..."
)}
</div>
</div>
</div>
</div>
)}
</section>
{/* Step 2: System Configuration */}
<section className={`content-section ${expandedSections.sysContent ? 'expanded' : 'collapsed'}`}>
<div className="section-header" onClick={() => toggleSection('sysContent')}>
<h2>⚙️ System Configuration</h2>
<span className="toggle-icon">{expandedSections.sysContent ? '▼' : '▶'}</span>
</div>
{expandedSections.sysContent && (
<div className="section-content">
<div className="sys-content-editor">
<h3>📝 System Content</h3>
<p className="description">
Edit the system prompt that will guide the AI in solving the puzzle:
</p>
<textarea
value={sysContent}
onChange={(e) => setSysContent(e.target.value)}
className="sys-content-textarea"
placeholder="Enter system content..."
/>
</div>
</div>
)}
</section>
{/* Step 3: Solve & Results */}
<section className={`content-section ${expandedSections.result ? 'expanded' : 'collapsed'}`}>
<div className="section-header" onClick={() => toggleSection('result')}>
<h2>🚀 Solve & Results</h2>
<span className="toggle-icon">{expandedSections.result ? '▼' : '▶'}</span>
</div>
{expandedSections.result && (
<div className="section-content">
<div className="solve-section">
<button
onClick={handleSolve}
disabled={isSolving || !puzzleText || !expectedSolution}
className={`btn btn-primary solve-btn ${isSolving ? 'loading' : ''}`}
>
{isSolving ? '🔄 Solving...' : '🧠 Solve Puzzle with AI'}
</button>
</div>
<div className="results-section">
<div className="result-summary">
<div className="result-item">
<span className="result-label">Status:</span>
<span className={`result-value ${executionSuccess === true ? 'success' : executionSuccess === false ? 'error' : 'pending'}`}>
{executionSuccess === null ? "⏳ Pending" : executionSuccess ? "✅ Success" : "❌ Failed"}
</span>
</div>
<div className="result-item">
<span className="result-label">Attempts:</span>
<span className="result-value">{attempts}</span>
</div>
</div>
{problematicConstraints && (
<div className="issues-section">
<h3>⚠️ Issues & Analysis</h3>
<div className="issues-display">
<pre>{problematicConstraints}</pre>
</div>
</div>
)}
{generatedCode && (
<div className="code-section">
<h3>💻 Generated Code</h3>
<div className="code-display">
<pre><code>{generatedCode}</code></pre>
</div>
</div>
)}
</div>
</div>
)}
</section>
</main>
{/* Navigation */}
<nav className="navigation">
<button
onClick={prevStep}
disabled={currentStep <= 1}
className="btn btn-outline"
>
← Previous
</button>
<span className="step-indicator">
Step {currentStep} of 3
</span>
<button
onClick={nextStep}
disabled={currentStep >= 3}
className="btn btn-outline"
>
Next →
</button>
</nav>
</>
);
}
export default PredefinedPuzzlePage;