記憶力テスト!どきどきカードマッチング

katayamawp

記憶力テスト:カードマッチング

マッチ数:
0
試行回数:
0

経過時間:
0








コード公開

        
        



CSS

#memory-match-game * { box-sizing: border-box; font-family: "Hiragino Kaku Gothic Pro", "メイリオ", sans-serif; } .game-container { max-width: 800px; margin: 0 auto; padding: 20px; background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%); border-radius: 15px; box-shadow: 0 10px 30px rgba(0, 0, 0, 0.15); } .game-header { text-align: center; margin-bottom: 20px; padding-bottom: 15px; border-bottom: 2px solid #8e9eab; } .game-header h2 { color: #2c3e50; margin-bottom: 15px; font-size: 28px; text-shadow: 1px 1px 2px rgba(255, 255, 255, 0.8); } .game-info { display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 10px; } .stats-container { display: flex; gap: 15px; } .stat-item, .timer-container { background-color: white; padding: 8px 15px; border-radius: 50px; box-shadow: 0 3px 6px rgba(0, 0, 0, 0.1); font-weight: bold; } .stat-label { margin-right: 5px; color: #34495e; } .stat-value { color: #e74c3c; } .difficulty-selector { display: flex; justify-content: center; gap: 10px; margin-bottom: 20px; flex-wrap: wrap; } .difficulty-btn { background-color: #ecf0f1; border: 2px solid #bdc3c7; padding: 8px 15px; border-radius: 50px; cursor: pointer; font-size: 14px; transition: all 0.3s ease; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); } .difficulty-btn:hover { background-color: #d5dbdb; } .difficulty-btn.selected { background-color: #3498db; color: white; border-color: #2980b9; box-shadow: 0 2px 8px rgba(52, 152, 219, 0.3); } .game-board { display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px; margin-bottom: 20px; perspective: 1000px; } .card { position: relative; height: 0; padding-bottom: 125%; cursor: pointer; transform-style: preserve-3d; transition: transform 0.6s; border-radius: 10px; box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); } .card:hover { box-shadow: 0 6px 12px rgba(0, 0, 0, 0.15); } .card.flipped { transform: rotateY(180deg); } .card-face { position: absolute; width: 100%; height: 100%; backface-visibility: hidden; border-radius: 10px; display: flex; justify-content: center; align-items: center; font-size: 32px; user-select: none; } .card-front { background: linear-gradient(45deg, #2c3e50, #4ca1af); color: white; transform: rotateY(0deg); } .card-back { background-color: white; transform: rotateY(180deg); } .card.matched .card-back { background-color: #d4f7d4; box-shadow: 0 0 15px rgba(46, 204, 113, 0.5); } .game-controls { display: flex; justify-content: center; gap: 15px; margin-bottom: 20px; } .btn { padding: 10px 25px; border-radius: 50px; cursor: pointer; font-size: 16px; transition: all 0.3s ease; font-weight: bold; border: none; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); } .primary-btn { background-color: #2ecc71; color: white; } .primary-btn:hover { background-color: #27ae60; transform: translateY(-2px); box-shadow: 0 6px 10px rgba(0, 0, 0, 0.15); } .secondary-btn { background-color: #3498db; color: white; } .secondary-btn:hover { background-color: #2980b9; transform: translateY(-2px); box-shadow: 0 6px 10px rgba(0, 0, 0, 0.15); } .btn:active { transform: translateY(1px); box-shadow: 0 2px 3px rgba(0, 0, 0, 0.1); } .btn:disabled { background-color: #95a5a6; cursor: not-allowed; transform: none; box-shadow: none; } .message-box { text-align: center; padding: 20px; border-radius: 10px; font-weight: bold; margin-top: 20px; font-size: 18px; animation: fadeIn 0.5s ease; } .success-message { background-color: #d4f7d4; color: #27ae60; border: 2px solid #27ae60; } @keyframes fadeIn { from { opacity: 0; transform: translateY(-10px); } to { opacity: 1; transform: translateY(0); } } @keyframes pulse { 0% { transform: scale(1); } 50% { transform: scale(1.1); } 100% { transform: scale(1); } } .card.no-match { animation: shake 0.5s; } @keyframes shake { 0%, 100% { transform: translateX(0) rotateY(180deg); } 20%, 60% { transform: translateX(-5px) rotateY(180deg); } 40%, 80% { transform: translateX(5px) rotateY(180deg); } } .card.match-found { animation: pulse 0.5s; } .hidden { display: none; } /* レスポンシブ対応 */ @media (max-width: 600px) { .game-info { flex-direction: column; align-items: center; } .game-board { grid-template-columns: repeat(3, 1fr); } .difficulty-btn { font-size: 12px; padding: 6px 12px; } }

JS

document.addEventListener('DOMContentLoaded', function() { // ゲーム設定 const gameConfig = { cardSymbols: ['🐶', '🐱', '🐭', '🐹', '🐰', '🦊', '🐻', '🐼', '🦁', '🐯', '🐨', '🐸', '🐵', '🐺', '🦄', '🐴', '🦋', '🐝', '🐌', '🐞', '🦀', '🐙', '🦐', '🐠'], gridSizes: { '3x2': { cols: 3, rows: 2 }, '4x3': { cols: 4, rows: 3 }, '4x4': { cols: 4, rows: 4 }, '6x4': { cols: 6, rows: 4 }, '6x6': { cols: 6, rows: 6 }, }, currentGrid: '4x3' }; // DOM要素 const gameBoard = document.getElementById('gameBoard'); const startButton = document.getElementById('startButton'); const restartButton = document.getElementById('restartButton'); const matchesElement = document.getElementById('matches'); const attemptsElement = document.getElementById('attempts'); const timerElement = document.getElementById('timer'); const messageBox = document.getElementById('messageBox'); const difficultyButtons = document.querySelectorAll('.difficulty-btn'); // ゲーム状態 let gameState = { cards: [], flippedCards: [], matches: 0, attempts: 0, gameActive: false, timerInterval: null, elapsedTime: 0, totalPairs: 0, }; // 難易度選択 difficultyButtons.forEach(btn => { btn.addEventListener('click', () => { if (!gameState.gameActive) { difficultyButtons.forEach(b => b.classList.remove('selected')); btn.classList.add('selected'); gameConfig.currentGrid = btn.dataset.grid; initGame(); // 難易度変更時にゲームを再初期化 } }); }); // ゲーム初期化 function initGame() { // ゲームボードをクリア gameBoard.innerHTML = ''; // グリッドサイズの設定 const { cols, rows } = gameConfig.gridSizes[gameConfig.currentGrid]; gameBoard.style.gridTemplateColumns = `repeat(${cols}, 1fr)`; // ゲーム状態をリセット gameState.matches = 0; gameState.attempts = 0; gameState.elapsedTime = 0; gameState.flippedCards = []; gameState.totalPairs = (cols * rows) / 2; // UI更新 matchesElement.textContent = gameState.matches; attemptsElement.textContent = gameState.attempts; timerElement.textContent = gameState.elapsedTime; // カードの準備 const cardPairs = prepareCards(cols * rows); gameState.cards = cardPairs; // カードの配置 createCards(cardPairs); // メッセージボックスを隠す messageBox.classList.add('hidden'); // ボタン状態の更新 startButton.disabled = false; restartButton.disabled = true; } // カードの準備(シンボルの選択とシャッフル) function prepareCards(totalCards) { const pairs = totalCards / 2; const selectedSymbols = gameConfig.cardSymbols.slice(0, pairs); // ペアを作成 let cardPairs = []; selectedSymbols.forEach(symbol => { cardPairs.push({ id: Math.random().toString(36).substr(2, 9), symbol: symbol, matched: false }, { id: Math.random().toString(36).substr(2, 9), symbol: symbol, matched: false }); }); // シャッフル for (let i = cardPairs.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); [cardPairs[i], cardPairs[j]] = [cardPairs[j], cardPairs[i]]; } return cardPairs; } // カードの作成と配置 function createCards(cards) { cards.forEach((card, index) => { const cardElement = document.createElement('div'); cardElement.className = 'card'; cardElement.dataset.id = card.id; // カードの表面(裏側) const cardFront = document.createElement('div'); cardFront.className = 'card-face card-front'; cardFront.textContent = '?'; // カードの裏面(表側、シンボルが見える) const cardBack = document.createElement('div'); cardBack.className = 'card-face card-back'; cardBack.textContent = card.symbol; cardElement.appendChild(cardFront); cardElement.appendChild(cardBack); // クリックイベント cardElement.addEventListener('click', () => { if (gameState.gameActive && !cardElement.classList.contains('flipped') && gameState.flippedCards.length < 2) { flipCard(cardElement, card); } }); gameBoard.appendChild(cardElement); }); } // カードをめくる function flipCard(cardElement, card) { // カードをめくる cardElement.classList.add('flipped'); gameState.flippedCards.push({ element: cardElement, card: card }); // 2枚めくった場合 if (gameState.flippedCards.length === 2) { gameState.attempts++; attemptsElement.textContent = gameState.attempts; const card1 = gameState.flippedCards[0].card; const card2 = gameState.flippedCards[1].card; // マッチしたかチェック if (card1.symbol === card2.symbol) { // マッチした場合 gameState.matches++; matchesElement.textContent = gameState.matches; // カードを「マッチした」状態にする gameState.flippedCards.forEach(fc => { fc.element.classList.add('matched'); fc.element.classList.add('match-found'); fc.card.matched = true; setTimeout(() => { fc.element.classList.remove('match-found'); }, 500); }); gameState.flippedCards = []; // すべてのペアを見つけた場合 if (gameState.matches === gameState.totalPairs) { endGame(); } } else { // マッチしなかった場合 gameState.flippedCards.forEach(fc => { fc.element.classList.add('no-match'); }); // 少し待ってからカードを裏返す setTimeout(() => { gameState.flippedCards.forEach(fc => { fc.element.classList.remove('flipped'); fc.element.classList.remove('no-match'); }); gameState.flippedCards = []; }, 1000); } } } // ゲーム開始 function startGame() { gameState.gameActive = true; startButton.disabled = true; restartButton.disabled = false; // タイマー開始 gameState.timerInterval = setInterval(() => { gameState.elapsedTime++; timerElement.textContent = gameState.elapsedTime; }, 1000); // 難易度ボタンを無効化 difficultyButtons.forEach(btn => { btn.disabled = true; }); } // ゲーム終了 function endGame() { gameState.gameActive = false; clearInterval(gameState.timerInterval); // 難易度ボタンを有効化 difficultyButtons.forEach(btn => { btn.disabled = false; }); // 終了メッセージを表示 const accuracy = ((gameState.matches / gameState.attempts) * 100).toFixed(1); messageBox.textContent = `おめでとうございます!全てのペアを見つけました!n時間: ${gameState.elapsedTime}秒 / 正確率: ${accuracy}%`; messageBox.className = 'message-box success-message'; messageBox.classList.remove('hidden'); // ボタン状態を更新 startButton.disabled = true; } // イベントリスナー startButton.addEventListener('click', startGame); restartButton.addEventListener('click', initGame); // 初期化 initGame(); });
記事URLをコピーしました