{\rtf1\ansi\ansicpg949\cocoartf2706 \cocoatextscaling0\cocoaplatform0{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} {\*\expandedcolortbl;;} \paperw11900\paperh16840\margl1440\margr1440\vieww11520\viewh8400\viewkind0 \pard\tx566\tx1133\tx1700\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\pardirnatural\partightenfactor0 \f0\fs24 \cf0 import React, \{ useState, useEffect, useCallback \} from 'react';\ import \{ Play, RotateCcw, Pause, CheckCircle2, FlaskConical, Mountain, Settings2, Edit3, Shuffle, ListOrdered, CheckSquare, ArrowRight, AlertCircle, X, Check, XCircle, RefreshCw, Hand, Map, Activity, Split, MousePointerClick, Home, ArrowLeft, Atom \} from 'lucide-react';\ \ // =================================================================================================\ // [APP 1] \uc0\u50896 \u49548 \u44592 \u54840 \u47560 \u49828 \u53552 (ElementFlashcards)\ // =================================================================================================\ const ElementFlashcards = (\{ onGoHome \}) => \{\ const elements1to20 = [\ \{ num: 1, sym: 'H', name: '\uc0\u49688 \u49548 ', p: 1, g: 1 \}, \{ num: 2, sym: 'He', name: '\u54764 \u47464 ', p: 1, g: 18 \},\ \{ num: 3, sym: 'Li', name: '\uc0\u47532 \u53932 ', p: 2, g: 1 \}, \{ num: 4, sym: 'Be', name: '\u48288 \u47540 \u47464 ', p: 2, g: 2 \},\ \{ num: 5, sym: 'B', name: '\uc0\u48533 \u49548 ', p: 2, g: 13 \}, \{ num: 6, sym: 'C', name: '\u53444 \u49548 ', p: 2, g: 14 \},\ \{ num: 7, sym: 'N', name: '\uc0\u51656 \u49548 ', p: 2, g: 15 \}, \{ num: 8, sym: 'O', name: '\u49328 \u49548 ', p: 2, g: 16 \},\ \{ num: 9, sym: 'F', name: '\uc0\u54540 \u47336 \u50724 \u47536 ', p: 2, g: 17 \}, \{ num: 10, sym: 'Ne', name: '\u45348 \u50728 ', p: 2, g: 18 \},\ \{ num: 11, sym: 'Na', name: '\uc0\u45208 \u53944 \u47464 ', p: 3, g: 1 \}, \{ num: 12, sym: 'Mg', name: '\u47560 \u44536 \u45348 \u49816 ', p: 3, g: 2 \},\ \{ num: 13, sym: 'Al', name: '\uc0\u50508 \u47336 \u48120 \u45700 ', p: 3, g: 13 \}, \{ num: 14, sym: 'Si', name: '\u44508 \u49548 ', p: 3, g: 14 \},\ \{ num: 15, sym: 'P', name: '\uc0\u51064 ', p: 3, g: 15 \}, \{ num: 16, sym: 'S', name: '\u54889 ', p: 3, g: 16 \},\ \{ num: 17, sym: 'Cl', name: '\uc0\u50684 \u49548 ', p: 3, g: 17 \}, \{ num: 18, sym: 'Ar', name: '\u50500 \u47476 \u44260 ', p: 3, g: 18 \},\ \{ num: 19, sym: 'K', name: '\uc0\u52860 \u47464 ', p: 4, g: 1 \}, \{ num: 20, sym: 'Ca', name: '\u52860 \u49816 ', p: 4, g: 2 \},\ ];\ const extraElements = [\ \{ sym: 'Mn', name: '\uc0\u47581 \u44032 \u45768 \u51592 ' \}, \{ sym: 'Pb', name: '\u45225 ' \}, \{ sym: 'Ag', name: '\u51008 ' \},\ \{ sym: 'Au', name: '\uc0\u44552 ' \}, \{ sym: 'Pt', name: '\u48177 \u44552 ' \}, \{ sym: 'Cu', name: '\u44396 \u47532 ' \},\ \{ sym: 'Fe', name: '\uc0\u52384 ' \}, \{ sym: 'Zn', name: '\u50500 \u50672 ' \}, \{ sym: 'Hg', name: '\u49688 \u51008 ' \},\ \{ sym: 'Ba', name: '\uc0\u48148 \u47464 ' \}, \{ sym: 'Sr', name: '\u49828 \u53944 \u47200 \u53932 ' \}, \{ sym: 'Br', name: '\u48652 \u47196 \u48124 ' \},\ \{ sym: 'I', name: '\uc0\u50500 \u51060 \u50724 \u46360 ' \},\ ];\ const allElements = [...elements1to20, ...extraElements];\ \ const [gameState, setGameState] = useState('start');\ const [cards, setCards] = useState([]);\ const [currentIndex, setCurrentIndex] = useState(0);\ const [isFlipped, setIsFlipped] = useState(false);\ const [isPaused, setIsPaused] = useState(false);\ const [isTransitioning, setIsTransitioning] = useState(false);\ const [targetCount, setTargetCount] = useState(20);\ const [isCustomMode, setIsCustomMode] = useState(false);\ const [customInput, setCustomInput] = useState('');\ const [orderMode, setOrderMode] = useState('mix');\ const [selectedCategories, setSelectedCategories] = useState(['A', 'B', 'C']);\ const [isReviewMode, setIsReviewMode] = useState(false);\ const [wrongCards, setWrongCards] = useState([]);\ const [currentMarkedWrong, setCurrentMarkedWrong] = useState(false);\ \ // Helper\ const generatePoolByType = useCallback((type) => \{\ let pool = [];\ const id = () => Math.random().toString(36).substr(2, 9);\ switch (type) \{\ case 1: elements1to20.forEach(e => pool.push(\{ id: id(), type: 1, q: `$\{e.num\}\uc0\u48264 `, a: `$\{e.sym\}($\{e.name\})`, color: 'bg-blue-100 text-blue-800' \})); break;\ case 2: allElements.forEach(e => pool.push(\{ id: id(), type: 2, q: `$\{e.sym\} \uc0\u51060 \u47492 \u51008 ?`, a: e.name, color: 'bg-green-100 text-green-800' \})); break;\ case 3: allElements.forEach(e => pool.push(\{ id: id(), type: 3, q: `$\{e.name\} \uc0\u44592 \u54840 \u45716 ?`, a: e.sym, color: 'bg-yellow-100 text-yellow-800' \})); break;\ case 4: elements1to20.forEach(e => pool.push(\{ id: id(), type: 4, q: `$\{e.p\}\uc0\u51452 \u44592 $\{e.g\}\u51313 `, a: `$\{e.sym\}($\{e.name\})`, color: 'bg-purple-100 text-purple-800' \})); break;\ case 5: elements1to20.forEach(e => pool.push(\{ id: id(), type: 5, q: `$\{e.sym\} \uc0\u48264 \u54840 \u45716 ?`, a: `$\{e.num\}\u48264 `, color: 'bg-red-100 text-red-800' \})); break;\ case 6: elements1to20.forEach(e => pool.push(\{ id: id(), type: 6, q: `$\{e.sym\} \uc0\u51452 \u44592 /\u51313 `, a: `$\{e.p\}\u51452 \u44592 $\{e.g\}\u51313 `, color: 'bg-indigo-100 text-indigo-800' \})); break;\ default: break;\ \}\ return pool;\ \}, []);\ \ const generateQuestions = useCallback((count, categories, mode) => \{\ const categoryMap = \{ 'A': [1, 5], 'B': [2, 3], 'C': [4, 6] \};\ const numCats = categories.length;\ if (numCats === 0) return [];\ const baseCount = Math.floor(count / numCats);\ let remainder = count % numCats;\ let final = [];\ \ categories.forEach((catCode) => \{\ let catCount = baseCount + (remainder > 0 ? 1 : 0);\ if (remainder > 0) remainder--;\ if (catCount === 0) return;\ \ const types = categoryMap[catCode];\ let catQuestions = [];\ const typeBaseCount = Math.floor(catCount / types.length);\ let typeRemainder = catCount % types.length;\ \ types.forEach(typeId => \{\ let typeCount = typeBaseCount + (typeRemainder > 0 ? 1 : 0);\ if (typeRemainder > 0) typeRemainder--;\ const pool = generatePoolByType(typeId).sort(() => 0.5 - Math.random());\ let selected = [];\ while (selected.length < typeCount) selected = selected.concat(pool);\ catQuestions = catQuestions.concat(selected.slice(0, typeCount));\ \});\ catQuestions.sort(() => 0.5 - Math.random());\ final = final.concat(catQuestions);\ \});\ \ if (mode === 'mix') final.sort(() => 0.5 - Math.random());\ return final;\ \}, [generatePoolByType]);\ \ const handleStart = () => \{\ if (selectedCategories.length === 0) return alert('\uc0\u50976 \u54805 \u51012 \u49440 \u53469 \u54644 \u51452 \u49464 \u50836 .');\ let cnt = targetCount;\ if (isCustomMode) \{\ cnt = parseInt(customInput, 10);\ if (isNaN(cnt) || cnt < 1 || cnt > 100) return alert('1~100 \uc0\u49324 \u51060 \u49707 \u51088 \u51077 \u47141 ');\ \}\ const newCards = generateQuestions(cnt, selectedCategories, orderMode);\ setCards(newCards);\ setWrongCards([]);\ setCurrentIndex(0);\ setIsFlipped(false);\ setIsPaused(false);\ setCurrentMarkedWrong(false);\ setGameState('playing');\ \};\ \ const handleRetry = () => \{\ const pool = [...wrongCards];\ const target = Math.ceil(pool.length * 1.5);\ let retry = [...pool];\ while (retry.length < target) retry.push(\{ ...pool[Math.floor(Math.random() * pool.length)], id: Math.random().toString() \});\ retry.sort(() => 0.5 - Math.random());\ setCards(retry);\ setWrongCards([]);\ setCurrentIndex(0);\ setIsFlipped(false);\ setIsPaused(false);\ setCurrentMarkedWrong(false);\ setGameState('playing');\ \};\ \ const nextCard = () => \{\ if (currentIndex < cards.length - 1) \{\ setIsTransitioning(true);\ setIsFlipped(false);\ setTimeout(() => \{\ setCurrentIndex(p => p + 1);\ setCurrentMarkedWrong(false);\ setIsTransitioning(false);\ \}, 500);\ \} else \{\ setGameState('finished');\ \}\ \};\ \ const handleCardClick = () => \{\ if (isPaused || isTransitioning) return;\ if (!isFlipped) setIsFlipped(true);\ else nextCard();\ \};\ \ const toggleWrong = (e) => \{\ e.stopPropagation();\ const card = cards[currentIndex];\ if (currentMarkedWrong) \{\ setWrongCards(p => p.filter(c => c.id !== card.id));\ setCurrentMarkedWrong(false);\ \} else \{\ setWrongCards(p => [...p, card]);\ setCurrentMarkedWrong(true);\ \}\ \};\ \ if (gameState === 'start') \{\ const presetCounts = [15, 20, 25, 30, 40, 50];\ const cats = [\ \{ id: 'A', label: '\uc0\u48264 \u54840 \u54984 \u47144 ', sub: '\u50896 \u51088 \u48264 \u54840 \u8596 \u50896 \u49548 ', color: 'bg-blue-50 border-blue-200 text-blue-700' \},\ \{ id: 'B', label: '\uc0\u44592 \u54840 \u54984 \u47144 ', sub: '\u50896 \u49548 \u47749 \u8596 \u44592 \u54840 ', color: 'bg-green-50 border-green-200 text-green-700' \},\ \{ id: 'C', label: '\uc0\u51452 \u44592 /\u51313 \u54984 \u47144 ', sub: '\u51452 \u44592 /\u51313 \u8596 \u50896 \u49548 ', color: 'bg-purple-50 border-purple-200 text-purple-700' \},\ ];\ return (\
\uc0\u51068 \u49884 \u51221 \u51648
\uc0\u52852 \u46300 \u47484 \u53552 \u52824 \u54616 \u50668 \u51221 \u45813 \u54869 \u51064
\