Created
October 4, 2025 01:32
-
-
Save f-gueguen/254fa57fcf5608d73a92082ba6651d42 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Memory Game</title> | |
| <script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script> | |
| <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script> | |
| <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <style> | |
| @keyframes shake{0%,100%{transform:translateX(0) rotate(0deg)}25%{transform:translateX(-10px) rotate(-5deg)}75%{transform:translateX(10px) rotate(5deg)}}@keyframes sparkle{0%,100%{transform:scale(1);filter:brightness(1)}50%{transform:scale(1.15);filter:brightness(1.3)}}@keyframes bounce-in{0%{transform:scale(0)}50%{transform:scale(1.2)}100%{transform:scale(1)}}@keyframes fade-in{from{opacity:0}to{opacity:1}}@keyframes particle-burst{0%{opacity:1;transform:translate(0,0) rotate(0deg) scale(1)}100%{opacity:0;transform:translate(calc(var(--vx) * 50),calc(var(--vy) * 50)) rotate(var(--rotation)) scale(0.3)}}.animate-shake{animation:shake .5s ease-in-out}.animate-sparkle{animation:sparkle .6s ease-in-out}.animate-bounce-in{animation:bounce-in .4s ease-out}.animate-fade-in{animation:fade-in .3s ease-out}input[type=range]::-webkit-slider-thumb{appearance:none;width:24px;height:24px;background:#a855f7;border-radius:50%;cursor:pointer}input[type=range]::-moz-range-thumb{width:24px;height:24px;background:#a855f7;border-radius:50%;cursor:pointer;border:none} | |
| </style> | |
| </head> | |
| <body> | |
| <div id="root"></div> | |
| <script type="text/babel"> | |
| const{useState,useEffect,useRef}=React;const SettingsIcon=()=>React.createElement("svg",{width:24,height:24,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:2},React.createElement("circle",{cx:12,cy:12,r:3}),React.createElement("path",{d:"M12 1v6m0 6v6m8.66-13.66l-4.24 4.24m-4.24 4.24l-4.24 4.24m13.66-4.24l-4.24-4.24m-4.24-4.24L3.34 3.34"}));const MemoryGame=()=>{const contentSets={uppercase:{items:['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'],color:'bg-blue-300'},lowercase:{items:['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'],color:'bg-green-300'},numbers:{items:['0','1','2','3','4','5','6','7','8','9'],color:'bg-yellow-300'},hiragana:{items:['あ','い','う','え','お','か','き','く','け','こ','さ','し','す','せ','そ','た','ち','つ','て','と','な','に','ぬ','ね','の','は','ひ','ふ','へ','ほ','ま','み','む','め','も','や','ゆ','よ','ら','り','る','れ','ろ','わ','を','ん'],color:'bg-pink-300'},katakana:{items:['ア','イ','ウ','エ','オ','カ','キ','ク','ケ','コ','サ','シ','ス','セ','ソ','タ','チ','ツ','テ','ト','ナ','ニ','ヌ','ネ','ノ','ハ','ヒ','フ','ヘ','ホ','マ','ミ','ム','メ','モ','ヤ','ユ','ヨ','ラ','リ','ル','レ','ロ','ワ','ヲ','ン'],color:'bg-purple-300'},emoji:{items:['🐶','🐱','🐭','🐹','🐰','🦊','🐻','🐼','🐨','🦁','🐯','🐸','🐵','🐔','🐧','🐦','🐤','🦆','🦅','🦉'],color:'bg-orange-300'}};const[activeToggles,setActiveToggles]=useState({emoji:!0});const[isRandomMode,setIsRandomMode]=useState(!1);const[difficulty,setDifficulty]=useState('normal');const[cards,setCards]=useState([]);const[flipped,setFlipped]=useState([]);const[matched,setMatched]=useState([]);const[shaking,setShaking]=useState([]);const[sparkling,setSparkling]=useState([]);const[showSettings,setShowSettings]=useState(!1);const[particles,setParticles]=useState([]);const[viewTime,setViewTime]=useState(1500);const[dragInfo,setDragInfo]=useState(null);const dragStartPos=useRef(null);const getDifficultyPairs=()=>{if(difficulty==='small')return 4;if(difficulty==='large')return 12;return 8};useEffect(()=>{initializeGame()},[activeToggles,isRandomMode,difficulty]);const initializeGame=()=>{const allContent=[];Object.entries(activeToggles).forEach(([key,value])=>{if(value){contentSets[key].items.forEach(item=>{allContent.push({content:item,type:key,color:contentSets[key].color})})}});if(allContent.length===0){setCards([]);return}const shuffled=allContent.sort(()=>Math.random()-.5);const numPairs=getDifficultyPairs();const selectedContent=shuffled.slice(0,Math.min(numPairs,Math.floor(allContent.length/2)));const gameCards=[...selectedContent,...selectedContent].sort(()=>Math.random()-.5).map((item,index)=>({id:index,content:item.content,type:item.type,color:item.color,position:isRandomMode?{x:Math.random()*70+15,y:Math.random()*70+15,rotation:Math.random()*360}:null}));setCards(gameCards);setFlipped([]);setMatched([]);setParticles([]);setDragInfo(null)};const createParticles=(index,content)=>{const card=document.getElementById(`card-${index}`);if(!card)return;const rect=card.getBoundingClientRect();const centerX=rect.left+rect.width/2;const centerY=rect.top+rect.height/2;const newParticles=Array.from({length:25},(_,i)=>{const angle=(i/25)*Math.PI*2;const speed=3+Math.random()*2;return{id:Date.now()+i+Math.random(),content,x:centerX,y:centerY,vx:Math.cos(angle)*speed,vy:Math.sin(angle)*speed,rotation:Math.random()*360,delay:Math.random()*100}});setParticles(prev=>[...prev,...newParticles]);setTimeout(()=>{setParticles(prev=>prev.filter(p=>!newParticles.some(np=>np.id===p.id)))},1200)};const handleCardClick=index=>{if(flipped.length===2||flipped.includes(index)||matched.includes(index))return;const newFlipped=[...flipped,index];setFlipped(newFlipped);if(newFlipped.length===2){const[first,second]=newFlipped;if(cards[first].content===cards[second].content){setSparkling([first,second]);setTimeout(()=>{createParticles(first,cards[first].content);createParticles(second,cards[second].content);setMatched([...matched,first,second]);setFlipped([]);setSparkling([])},viewTime)}else{setShaking([first,second]);setTimeout(()=>{setFlipped([]);setShaking([])},800)}}};const handlePointerDown=(e,index)=>{if(!isRandomMode||flipped.includes(index)||matched.includes(index))return;e.preventDefault();e.stopPropagation();const clientX=e.clientX||(e.touches&&e.touches[0].clientX);const clientY=e.clientY||(e.touches&&e.touches[0].clientY);dragStartPos.current={x:clientX,y:clientY,time:Date.now()};setDragInfo({index,startX:clientX,startY:clientY})};const handlePointerMove=e=>{if(!isRandomMode||!dragInfo)return;e.preventDefault();const clientX=e.clientX||(e.touches&&e.touches[0].clientX);const clientY=e.clientY||(e.touches&&e.touches[0].clientY);const container=document.getElementById('game-container');if(!container)return;const rect=container.getBoundingClientRect();const x=((clientX-rect.left)/rect.width)*100;const y=((clientY-rect.top)/rect.height)*100;if(x>=5&&x<=95&&y>=5&&y<=95){setCards(prev=>prev.map((card,i)=>i===dragInfo.index?{...card,position:{...card.position,x,y}}:card))}};const handlePointerUp=(e,index)=>{if(!isRandomMode)return;const clientX=e.clientX||(e.changedTouches&&e.changedTouches[0].clientX);const clientY=e.clientY||(e.changedTouches&&e.changedTouches[0].clientY);if(dragStartPos.current&&dragInfo){const deltaX=Math.abs(clientX-dragStartPos.current.x);const deltaY=Math.abs(clientY-dragStartPos.current.y);const deltaTime=Date.now()-dragStartPos.current.time;const wasClick=deltaX<10&&deltaY<10&&deltaTime<300;if(wasClick){handleCardClick(index)}}setDragInfo(null);dragStartPos.current=null};useEffect(()=>{if(!isRandomMode||!dragInfo)return;const handleGlobalMove=e=>handlePointerMove(e);const handleGlobalUp=()=>{setDragInfo(null);dragStartPos.current=null};window.addEventListener('mousemove',handleGlobalMove);window.addEventListener('mouseup',handleGlobalUp);window.addEventListener('touchmove',handleGlobalMove,{passive:!1});window.addEventListener('touchend',handleGlobalUp);return()=>{window.removeEventListener('mousemove',handleGlobalMove);window.removeEventListener('mouseup',handleGlobalUp);window.removeEventListener('touchmove',handleGlobalMove);window.removeEventListener('touchend',handleGlobalUp)}},[dragInfo,isRandomMode]);const toggleContent=key=>{setActiveToggles(prev=>({...prev,[key]:!prev[key]}))};const getFontSize=content=>{if(/[\u3040-\u309F\u30A0-\u30FF]/.test(content))return'text-4xl';if(/[0-9]/.test(content))return'text-5xl font-bold';if(/[a-z]/.test(content))return'text-5xl font-bold';if(/[A-Z]/.test(content))return'text-5xl font-bold';return'text-5xl'};return React.createElement("div",{className:"min-h-screen bg-teal-100 p-4 relative overflow-hidden"},React.createElement("button",{onClick:()=>setShowSettings(!0),className:"fixed top-4 right-4 bg-white bg-opacity-80 p-3 rounded-full shadow-lg hover:shadow-xl transform hover:scale-105 transition-all duration-200 z-40"},React.createElement(SettingsIcon)),React.createElement("div",{id:"game-container",className:`${isRandomMode?'absolute inset-0 p-4':'flex items-center justify-center min-h-screen'}`},!isRandomMode?React.createElement("div",{className:"grid grid-cols-4 sm:grid-cols-6 gap-4"},cards.map((card,index)=>{const isFlipped=flipped.includes(index)||matched.includes(index);const isShaking=shaking.includes(index);const isSparkling=sparkling.includes(index);const isMatched=matched.includes(index);return React.createElement("div",{key:card.id,id:`card-${index}`,onClick:()=>handleCardClick(index),className:`w-20 h-20 sm:w-24 sm:h-24 rounded-2xl cursor-pointer flex items-center justify-center ${getFontSize(card.content)} transition-all duration-300 transform ${isFlipped?card.color:'bg-indigo-400'} ${isFlipped?'scale-105':'hover:scale-110'} ${isShaking?'animate-shake':''} ${isSparkling?'animate-sparkle':''} ${isMatched?'opacity-0 pointer-events-none':''} shadow-lg hover:shadow-xl`},isFlipped?React.createElement("span",{className:"animate-bounce-in"},card.content):React.createElement("span",{className:"text-4xl"},"❓"))})):cards.map((card,index)=>{const isFlipped=flipped.includes(index)||matched.includes(index);const isShaking=shaking.includes(index);const isSparkling=sparkling.includes(index);const isMatched=matched.includes(index);const isDragging=dragInfo?.index===index;return React.createElement("div",{key:card.id,id:`card-${index}`,onMouseDown:e=>handlePointerDown(e,index),onMouseUp:e=>handlePointerUp(e,index),onTouchStart:e=>handlePointerDown(e,index),onTouchEnd:e=>handlePointerUp(e,index),style:{left:`${card.position?.x||50}%`,top:`${card.position?.y||50}%`,transform:`translate(-50%, -50%) rotate(${card.position?.rotation||0}deg)`,touchAction:'none'},className:`absolute w-20 h-20 sm:w-24 sm:h-24 rounded-2xl select-none flex items-center justify-center ${getFontSize(card.content)} transition-opacity duration-300 ${isFlipped?card.color:'bg-indigo-400'} ${isShaking?'animate-shake':''} ${isSparkling?'animate-sparkle':''} ${isMatched?'opacity-0 pointer-events-none':''} shadow-lg ${isDragging?'cursor-grabbing scale-110 z-50':'cursor-grab hover:scale-105'}`},isFlipped?React.createElement("span",{className:"animate-bounce-in pointer-events-none"},card.content):React.createElement("span",{className:"text-4xl pointer-events-none"},"❓"))})),particles.map(particle=>React.createElement("div",{key:particle.id,className:"fixed pointer-events-none text-2xl z-50",style:{left:particle.x,top:particle.y,animation:`particle-burst 1.2s ease-out forwards ${particle.delay}ms`,'--vx':`${particle.vx}px`,'--vy':`${particle.vy}px`,'--rotation':`${particle.rotation}deg`}},particle.content)),showSettings&&React.createElement("div",{className:"fixed inset-0 flex items-center justify-center bg-black bg-opacity-50 z-50 animate-fade-in p-4"},React.createElement("div",{className:"bg-white rounded-3xl p-8 max-w-md w-full max-h-[90vh] overflow-y-auto animate-bounce-in"},React.createElement("h2",{className:"text-3xl font-bold text-purple-600 mb-6 text-center"},"⚙️"),React.createElement("div",{className:"grid grid-cols-3 gap-3 mb-6"},[{key:'uppercase',icon:'A',color:'bg-blue-300'},{key:'lowercase',icon:'a',color:'bg-green-300'},{key:'numbers',icon:'1',color:'bg-yellow-300'},{key:'hiragana',icon:'あ',color:'bg-pink-300'},{key:'katakana',icon:'ア',color:'bg-purple-300'},{key:'emoji',icon:'🐶',color:'bg-orange-300'}].map(({key,icon,color})=>React.createElement("button",{key:key,onClick:()=>toggleContent(key),className:`aspect-square py-3 px-2 rounded-xl text-3xl font-semibold transition-all duration-200 transform hover:scale-105 flex items-center justify-center ${activeToggles[key]?color:'bg-gray-300'}`},icon))),React.createElement("div",{className:"space-y-4 pt-6 border-t border-gray-200"},React.createElement("button",{onClick:()=>setIsRandomMode(!isRandomMode),className:`w-full py-3 px-4 rounded-xl text-lg font-semibold transition-all duration-200 transform hover:scale-105 flex items-center justify-center gap-2 ${isRandomMode?'bg-teal-400':'bg-gray-300'}`},React.createElement("span",{className:"text-2xl"},"🎲")),React.createElement("div",{className:"grid grid-cols-3 gap-3"},[{key:'small',label:'4',pairs:4},{key:'normal',label:'8',pairs:8},{key:'large',label:'12',pairs:12}].map(({key,label})=>React.createElement("button",{key:key,onClick:()=>setDifficulty(key),className:`py-3 px-2 rounded-xl text-lg font-semibold transition-all duration-200 transform hover:scale-105 ${difficulty===key?'bg-blue-400':'bg-gray-300'}`},label))),React.createElement("div",null,React.createElement("div",{className:"flex items-center justify-between mb-2"},React.createElement("span",{className:"text-lg font-semibold text-gray-700"},"⏱️"),React.createElement("span",{className:"text-sm text-gray-600"},viewTime+"ms")),React.createElement("input",{type:"range",min:"500",max:"3000",step:"100",value:viewTime,onChange:e=>setViewTime(parseInt(e.target.value)),className:"w-full h-3 bg-purple-200 rounded-lg appearance-none cursor-pointer"}))),React.createElement("button",{onClick:()=>{setShowSettings(!1);initializeGame()},className:"w-full bg-purple-500 text-white py-3 px-6 mt-6 rounded-xl text-xl font-bold shadow-lg hover:shadow-xl transform hover:scale-105 transition-all duration-200"},"✓"))))};ReactDOM.render(React.createElement(MemoryGame),document.getElementById('root')); | |
| </script> | |
| </body> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment