import React, { useState, useEffect, useRef } from ‘react’;
import { Star, Lock, Play, Home, RefreshCw, CheckCircle, Zap, Move } from ‘lucide-react’;
// — Graphics & Assets (SVG Components) —
const BubbleIcon = ({ size, color }) => (
);
const MagnetIcon = () => (
);
const BulbIcon = ({ isOn }) => (
);
const BatteryIcon = () => (
);
// — Game Data —
const LESSONS = [
{ id: 1, title: “زنگ علوم”, type: “bubbles”, bg: “from-blue-200 to-cyan-100”, icon: “🫧” },
{ id: 2, title: “مخلوطها”, type: “dragDrop”, category: “mixture”, bg: “from-orange-100 to-yellow-100”, icon: “🥣” },
{ id: 3, title: “انرژی”, type: “dragDrop”, category: “energy”, bg: “from-yellow-200 to-orange-200”, icon: “⚡” },
{ id: 4, title: “الکتریسیته”, type: “circuit”, bg: “from-gray-800 to-gray-900”, icon: “💡” },
{ id: 5, title: “گرما و ماده”, type: “dragDrop”, category: “heat”, bg: “from-red-200 to-orange-100”, icon: “🔥” },
{ id: 6, title: “سنگها”, type: “dragDrop”, category: “rocks”, bg: “from-stone-300 to-stone-100”, icon: “🪨” },
{ id: 7, title: “آهنربا”, type: “magnet”, bg: “from-blue-100 to-indigo-100”, icon: “🧲” },
{ id: 8, title: “آسمان شب”, type: “orbit”, bg: “from-indigo-900 to-purple-900”, icon: “🪐” },
{ id: 9, title: “بدن ما ۱”, type: “dragDrop”, category: “body1”, bg: “from-rose-100 to-pink-100”, icon: “🫀” },
{ id: 10, title: “بدن ما ۲”, type: “dragDrop”, category: “body2”, bg: “from-red-100 to-rose-200”, icon: “🩸” },
{ id: 11, title: “بیمهرهها”, type: “dragDrop”, category: “bugs”, bg: “from-green-100 to-emerald-100”, icon: “🐞” },
{ id: 12, title: “گیاهان”, type: “dragDrop”, category: “plants”, bg: “from-green-200 to-lime-100”, icon: “🌻” },
{ id: 13, title: “زیستگاه”, type: “dragDrop”, category: “habitat”, bg: “from-teal-100 to-cyan-200”, icon: “🏞️” },
];
const SORTING_DATA = {
mixture: {
title: “جداسازی مخلوطها”,
buckets: [{ id: “sol”, label: “محلول (یکنواخت)”, color: “bg-blue-200” }, { id: “mix”, label: “مخلوط (غیریکنواخت)”, color: “bg-orange-200” }],
items: [
{ id: 1, text: “آب و نمک”, type: “sol”, emoji: “💧🧂” },
{ id: 2, text: “آجیل”, type: “mix”, emoji: “🥜” },
{ id: 3, text: “سالاد”, type: “mix”, emoji: “🥗” },
{ id: 4, text: “شربت”, type: “sol”, emoji: “🍷” },
]
},
rocks: {
title: “شناسایی سنگها”,
buckets: [{ id: “sed”, label: “رسوبی (لایه لایه)”, color: “bg-yellow-200” }, { id: “ign”, label: “آذرین (سخت)”, color: “bg-gray-400” }],
items: [
{ id: 1, text: “سنگ آهک”, type: “sed”, emoji: “🪨” },
{ id: 2, text: “گرانیت”, type: “ign”, emoji: “🧱” },
{ id: 3, text: “سنگ پا”, type: “ign”, emoji: “🌑” },
{ id: 4, text: “کنگلومرا”, type: “sed”, emoji: “🍘” },
]
},
bugs: {
title: “حشرات یا عنکبوتیان؟”,
buckets: [{ id: “ins”, label: “حشره (۶ پا)”, color: “bg-green-300” }, { id: “spi”, label: “عنکبوتیان (۸ پا)”, color: “bg-purple-300” }],
items: [
{ id: 1, text: “مورچه”, type: “ins”, emoji: “🐜” },
{ id: 2, text: “عنکبوت”, type: “spi”, emoji: “🕷️” },
{ id: 3, text: “پروانه”, type: “ins”, emoji: “🦋” },
{ id: 4, text: “عقرب”, type: “spi”, emoji: “🦂” },
]
},
// Add simplified fallbacks for other dragDrop categories to avoid errors
energy: { title: “انرژی”, buckets: [{id: “ren”, label: “تجدیدپذیر”, color: “bg-green-200”}, {id: “non”, label: “سوخت”, color: “bg-gray-300″}], items: [{id:1, text:”باد”, type:”ren”, emoji:”🌬️”}, {id:2, text:”نفت”, type:”non”, emoji:”🛢️”}]},
heat: { title: “رسانا و نارسانا”, buckets: [{id: “con”, label: “رسانا”, color: “bg-red-200”}, {id: “ins”, label: “نارسانا”, color: “bg-blue-200″}], items: [{id:1, text:”قاشق فلزی”, type:”con”, emoji:”🥄”}, {id:2, text:”چوب”, type:”ins”, emoji:”🪵”}]},
body1: { title: “سلولها”, buckets: [{id: “good”, label: “مفید”, color: “bg-green-200”}, {id: “bad”, label: “مضر”, color: “bg-red-200″}], items: [{id:1, text:”گلبول سفید”, type:”good”, emoji:”🛡️”}, {id:2, text:”میکروب”, type:”bad”, emoji:”🦠”}]},
body2: { title: “دستگاه تنفس”, buckets: [{id: “in”, label: “دم”, color: “bg-blue-200”}, {id: “out”, label: “بازدم”, color: “bg-gray-200″}], items: [{id:1, text:”اکسیژن”, type:”in”, emoji:”⭕”}, {id:2, text:”دیاکسید کربن”, type:”out”, emoji:”⚫”}]},
plants: { title: “دانهها”, buckets: [{id: “one”, label: “تکلپه”, color: “bg-yellow-200”}, {id: “two”, label: “دولپه”, color: “bg-green-200″}], items: [{id:1, text:”ذرت”, type:”one”, emoji:”🌽”}, {id:2, text:”لوبیا”, type:”two”, emoji:”🫘”}]},
habitat: { title: “زنجیره غذایی”, buckets: [{id: “pro”, label: “تولیدکننده”, color: “bg-green-300”}, {id: “con”, label: “مصرفکننده”, color: “bg-orange-300″}], items: [{id:1, text:”گیاه”, type:”pro”, emoji:”🌿”}, {id:2, text:”شیر”, type:”con”, emoji:”🦁”}]},
};
// — Mini Games Engines —
// 1. Bubble Game Engine (Canvas-less for simplicity in React State)
const BubbleGame = ({ onWin }) => {
const [bubbles, setBubbles] = useState([]);
const [popped, setPopped] = useState(0);
const target = 10;
useEffect(() => {
const interval = setInterval(() => {
if (popped >= target) return;
const id = Math.random();
const size = 40 + Math.random() * 60;
const left = 10 + Math.random() * 80;
const color = [‘#EF4444’, ‘#3B82F6’, ‘#10B981’, ‘#F59E0B’][Math.floor(Math.random() * 4)];
setBubbles(prev => […prev, { id, size, left, top: 100, color }]);
}, 800);
return () => clearInterval(interval);
}, [popped]);
useEffect(() => {
const moveInterval = setInterval(() => {
setBubbles(prev => prev.map(b => ({ …b, top: b.top – 1 })).filter(b => b.top > -20));
}, 50);
return () => clearInterval(moveInterval);
}, []);
const pop = (id) => {
setBubbles(prev => prev.filter(b => b.id !== id));
setPopped(p => {
const newP = p + 1;
if (newP >= target) setTimeout(onWin, 1000);
return newP;
});
};
return (
{popped >= target &&
}
{bubbles.map(b => (
className=”absolute cursor-pointer transform hover:scale-110 transition-transform active:scale-90″
style={{ left: `${b.left}%`, top: `${b.top}%`, width: b.size, height: b.size }}
>
))}
);
};
// 2. Circuit Builder Engine
const CircuitGame = ({ onWin }) => {
const [wires, setWires] = useState([false, false]); // Wire 1, Wire 2
const [switchOn, setSwitchOn] = useState(false);
const isComplete = wires[0] && wires[1] && switchOn;
useEffect(() => {
if (isComplete) setTimeout(onWin, 2000);
}, [isComplete]);
return (
{/* Wires Graphics */}
className={`w-16 h-16 rounded-full border-4 cursor-pointer transition-all active:scale-95 shadow-lg flex items-center justify-center ${switchOn ? ‘bg-green-500 border-green-700’ : ‘bg-red-500 border-red-700’}`}
>
{/* Wire Interaction Zones */}
{isComplete ? “مدار کامل شد! لامپ روشن شد.” : “سیمها را وصل کن و کلید را بزن.”}
);
};
// 3. Magnet Lab Engine
const MagnetGame = ({ onWin }) => {
const [magnetPos, setMagnetPos] = useState({ x: 50, y: 50 });
const [items, setItems] = useState([
{ id: 1, x: 20, y: 30, type: ‘metal’, icon: ‘📎’, caught: false },
{ id: 2, x: 80, y: 30, type: ‘metal’, icon: ‘🔩’, caught: false },
{ id: 3, x: 20, y: 80, type: ‘non-metal’, icon: ‘✏️’, caught: false },
{ id: 4, x: 80, y: 80, type: ‘non-metal’, icon: ‘🧸’, caught: false },
{ id: 5, x: 50, y: 20, type: ‘metal’, icon: ‘🗝️’, caught: false },
]);
const containerRef = useRef(null);
const handleMouseMove = (e) => {
if (!containerRef.current) return;
const rect = containerRef.current.getBoundingClientRect();
const x = ((e.clientX – rect.left) / rect.width) * 100;
const y = ((e.clientY – rect.top) / rect.height) * 100;
setMagnetPos({ x, y });
// Check attraction
setItems(prev => prev.map(item => {
if (item.caught) return item;
const dist = Math.sqrt(Math.pow(item.x – x, 2) + Math.pow(item.y – y, 2));
if (dist < 15 && item.type === 'metal') return { ...item, caught: true };
return item;
}));
};
const caughtCount = items.filter(i => i.caught).length;
const totalMetal = items.filter(i => i.type === ‘metal’).length;
useEffect(() => {
if (caughtCount === totalMetal) setTimeout(onWin, 1500);
}, [caughtCount]);
return (
className=”relative w-full h-[400px] bg-slate-200 rounded-3xl overflow-hidden cursor-none shadow-inner border-4 border-slate-300″
>
{/* Items */}
{items.map(item => {
const style = item.caught
? { left: `${magnetPos.x}%`, top: `${magnetPos.y + 10}%`, transition: ‘all 0.1s’ }
: { left: `${item.x}%`, top: `${item.y}%`, transition: ‘all 0.5s’ };
return (
);
})}
{/* Magnet Cursor */}
{caughtCount === totalMetal && (
)}
);
};
// 4. Drag & Drop Sorting Engine
const SortingGame = ({ category, onWin }) => {
const data = SORTING_DATA[category];
const [items, setItems] = useState(data.items);
const [completed, setCompleted] = useState([]);
const [errorMsg, setErrorMsg] = useState(null); // New state for error message
const handleDrop = (itemId, bucketId) => {
const item = items.find(i => i.id === itemId);
if (item.type === bucketId) {
setItems(prev => prev.filter(i => i.id !== itemId));
setCompleted(prev => […prev, item]);
if (items.length === 1) setTimeout(onWin, 1000); // Last item removed
if (errorMsg) setErrorMsg(null); // Clear error on success
} else {
// Replaced alert() with a temporary visible error message
setErrorMsg(“اشتباه! این مورد در دستهبندی صحیح قرار نگرفت.”);
setTimeout(() => setErrorMsg(null), 1500);
}
};
return (
{data.title}
{/* Buckets */}
onDrop={(e) => {
const itemId = parseInt(e.dataTransfer.getData(“itemId”));
handleDrop(itemId, bucket.id);
}}
>
{bucket.label}
{i.emoji}
))}
))}
{/* Error Message Display */}
{errorMsg && (
)}
{/* Items Area */}
) : (
items.map(item => (
className=”bg-white px-4 py-3 rounded-2xl shadow-[0_4px_0_rgba(0,0,0,0.1)] cursor-move hover:-translate-y-1 transition-all active:shadow-none active:translate-y-1 flex flex-col items-center border border-gray-100″
>
{item.emoji}
{item.text}
))
)}
آیتمها را بکشید و در سبد درست بیندازید
);
};
// 5. Orbit Game (Drag Slider)
const OrbitGame = ({ onWin }) => {
const [rotation, setRotation] = useState(0);
const targetRot = 180; // Target position in degrees
const handleDrag = (e) => {
const val = parseInt(e.target.value);
setRotation(val);
if (Math.abs(val – targetRot) < 10) setTimeout(onWin, 1000);
};
return (
{/* Earth Orbit Path */}
{/* Earth */}
🌍
);
};
// — Main App Component —
export default function VisualScienceExplorer() {
const [screen, setScreen] = useState(‘map’); // map, game
const [activeLesson, setActiveLesson] = useState(null);
const [completedLessons, setCompletedLessons] = useState([1]); // Level 1 unlocked
const unlockNext = (currentId) => {
if (!completedLessons.includes(currentId + 1)) {
setCompletedLessons([…completedLessons, currentId + 1]);
}
setTimeout(() => {
setScreen(‘map’);
setActiveLesson(null);
}, 2000);
};
const renderGameContent = () => {
if (!activeLesson) return null;
switch (activeLesson.type) {
case ‘bubbles’: return
case ‘circuit’: return
case ‘magnet’: return
case ‘dragDrop’: return
case ‘orbit’: return
default: return
;
}
};
const MapScreen = () => (
نقشه علمی من
const isUnlocked = completedLessons.includes(lesson.id);
const isNext = Math.max(…completedLessons) === lesson.id;
return (
{/* Connector Line */}
{idx < LESSONS.length - 1 && (
)}
{lesson.title}
);
})}
);
const GameScreen = () => (
{activeLesson.title}
{/* Game Area */}
);
return (
);
}
