Spaces:
Running
Running
| <html lang="zh-Hant"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>數學探險島 - 哲學之塔</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <link rel="preconnect" href="https://fonts.googleapis.com"> | |
| <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> | |
| <link href="https://fonts.googleapis.com/css2?family=Noto+Sans+TC:wght@400;500;700&display=swap" rel="stylesheet"> | |
| <style> | |
| body { | |
| font-family: 'Noto Sans TC', sans-serif; | |
| background-image: url('https://i.meee.com.tw/wCWCOGx.png'); | |
| background-size: cover; | |
| background-position: center; | |
| background-attachment: fixed; | |
| } | |
| .tower-bg { | |
| background-image: url('https://www.transparenttextures.com/patterns/stone-wall.png'); | |
| background-color: #e2e8f0; | |
| } | |
| /* 自訂拉桿樣式 */ | |
| input[type=range] { | |
| -webkit-appearance: none; | |
| width: 100%; | |
| background: transparent; | |
| } | |
| input[type=range]:focus { | |
| outline: none; | |
| } | |
| input[type=range]::-webkit-slider-runnable-track { | |
| width: 100%; | |
| height: 12px; | |
| cursor: pointer; | |
| background: #93c5fd; | |
| border-radius: 5px; | |
| border: 1px solid #60a5fa; | |
| } | |
| input[type=range]::-webkit-slider-thumb { | |
| border: 2px solid #3b82f6; | |
| height: 30px; | |
| width: 30px; | |
| border-radius: 50%; | |
| background: #eff6ff; | |
| cursor: pointer; | |
| -webkit-appearance: none; | |
| margin-top: -10px; | |
| } | |
| /* 成功時的閃爍動畫 */ | |
| @keyframes flash { | |
| 0%, 100% { box-shadow: 0 0 20px 5px rgba(34, 197, 94, 0.7); } | |
| 50% { box-shadow: 0 0 5px 0px rgba(34, 197, 94, 0.2); } | |
| } | |
| .success-flash { | |
| animation: flash 1.5s ease-in-out; | |
| } | |
| /* 測驗選項樣式 */ | |
| .quiz-option { | |
| display: block; | |
| padding: 0.75rem 1rem; | |
| border: 2px solid #e5e7eb; | |
| border-radius: 0.5rem; | |
| margin-bottom: 0.5rem; | |
| cursor: pointer; | |
| transition: all 0.2s; | |
| } | |
| .quiz-option:hover { | |
| background-color: #f3f4f6; | |
| } | |
| input[type="radio"]:checked + .quiz-option { | |
| background-color: #dbeafe; | |
| border-color: #60a5fa; | |
| } | |
| .question-container.incorrect { | |
| border: 2px solid #ef4444; | |
| border-radius: 0.75rem; | |
| padding: 1rem; | |
| background-color: #fee2e2; | |
| } | |
| </style> | |
| </head> | |
| <body class="flex items-center justify-center min-h-screen p-4"> | |
| <div id="main-container" class="container mx-auto max-w-5xl"> | |
| <!-- 故事引導畫面 --> | |
| <div id="story-view" class="bg-white/80 backdrop-blur-md rounded-xl shadow-2xl p-8 text-center"> | |
| <h1 class="text-3xl md:text-4xl font-bold text-gray-800 text-center mb-6">前情提要:西帕索斯的悲劇</h1> | |
| <div class="border rounded-lg shadow-inner bg-gray-100 p-12 flex flex-col items-center justify-center" style="height: 50vh;"> | |
| <svg xmlns="http://www.w3.org/2000/svg" class="h-24 w-24 text-indigo-300 mb-4" fill="none" viewBox="0 0 24 24" stroke="currentColor"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6.253v11.494m-9-5.747h18" /> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6.253v11.494m-9-5.747h18" /> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 18h16" /> | |
| </svg> | |
| <p class="text-xl text-gray-600">故事即將展開...</p> | |
| <p class="mt-4 text-lg text-indigo-600 font-semibold">點擊下方按鈕,在新分頁閱讀故事前情提要!</p> | |
| </div> | |
| <p class="mt-8 text-gray-600 font-semibold">閱讀完故事後,請回來按下開始挑戰</p> | |
| <a href="https://g.co/gemini/share/d181055c5aa2" target="_blank" id="story-link-button" class="mt-2 inline-block w-full md:w-auto bg-blue-500 text-white font-bold py-3 px-8 rounded-lg hover:bg-blue-600 transition-colors shadow-lg text-lg"> | |
| 閱讀故事 | |
| </a> | |
| <button id="start-quiz-button" class="mt-4 w-full md:w-auto bg-indigo-600 text-white font-bold py-3 px-8 rounded-lg hover:bg-indigo-700 transition-colors shadow-lg text-lg"> | |
| 開始挑戰! | |
| </button> | |
| </div> | |
| <!-- 閱讀測驗畫面 (新增) --> | |
| <div id="quiz-view" class="hidden bg-white/80 backdrop-blur-md rounded-xl shadow-2xl p-8"> | |
| <h1 class="text-3xl font-bold text-gray-800 text-center mb-6">閱讀測驗:西帕索斯的考驗</h1> | |
| <div class="space-y-6 text-left"> | |
| <!-- 問題 1 --> | |
| <div id="question-container-0" class="question-container"> | |
| <p class="font-semibold mb-2">1. 根據故事內容,西帕索斯為什麼被畢達哥拉斯囚禁在哲學之塔?</p> | |
| <div class="space-y-2"> | |
| <label><input type="radio" name="q0" value="A" class="hidden"> <span class="quiz-option">(A) 因為他試圖偷取畢達哥拉斯的數學理論。</span></label> | |
| <label><input type="radio" name="q0" value="B" class="hidden"> <span class="quiz-option">(B) 因為他在公開場合侮辱了畢達哥拉斯本人。</span></label> | |
| <label><input type="radio" name="q0" value="C" class="hidden"> <span class="quiz-option">(C) 因為他發現了一個無法用分數或小數表示的數字,這與畢達哥拉斯學派的信念相衝突。</span></label> | |
| <label><input type="radio" name="q0" value="D" class="hidden"> <span class="quiz-option">(D) 因為他沒能成功計算出一個正方形的面積。</span></label> | |
| </div> | |
| </div> | |
| <!-- 問題 2 --> | |
| <div id="question-container-1" class="question-container"> | |
| <p class="font-semibold mb-2">2. 故事中,那個動搖了畢達哥拉斯學派信念的「神秘數字」,最初是源自於什麼?</p> | |
| <div class="space-y-2"> | |
| <label><input type="radio" name="q1" value="A" class="hidden"> <span class="quiz-option">(A) 一個面積為3的圓形半徑。</span></label> | |
| <label><input type="radio" name="q1" value="B" class="hidden"> <span class="quiz-option">(B) 一個面積為2的正方形邊長。</span></label> | |
| <label><input type="radio" name="q1" value="C" class="hidden"> <span class="quiz-option">(C) 一個從未有人見過的全新立體圖形。</span></label> | |
| <label><input type="radio" name="q1" value="D" class="hidden"> <span class="quiz-option">(D) 一個畢達哥拉斯自己提出的數學謎題。</span></label> | |
| </div> | |
| </div> | |
| <!-- 問題 3 --> | |
| <div id="question-container-2" class="question-container"> | |
| <p class="font-semibold mb-2">3. 在故事的結尾,你(讀者)被賦予的主要任務是什麼?</p> | |
| <div class="space-y-2"> | |
| <label><input type="radio" name="q2" value="A" class="hidden"> <span class="quiz-option">(A) 找到一把萬能鑰匙,趁半夜把西帕索斯從監獄裡救出來。</span></label> | |
| <label><input type="radio" name="q2" value="B" class="hidden"> <span class="quiz-option">(B) 回到未來,尋找歷史文獻來證明西帕索斯是對的。</span></label> | |
| <label><input type="radio" name="q2" value="C" class="hidden"> <span class="quiz-option">(C) 成為畢達哥拉斯的學徒,從內部瓦解他的學派。</span></label> | |
| <label><input type="radio" name="q2" value="D" class="hidden"> <span class="quiz-option">(D) 找出幾個「神秘數字」的近似值,用具體的證據去說服畢達哥拉斯。</span></label> | |
| </div> | |
| </div> | |
| </div> | |
| <div id="quiz-feedback" class="text-center font-semibold mt-6 min-h-[24px]"></div> | |
| <div class="flex flex-col md:flex-row gap-4 mt-6"> | |
| <button id="reread-story-button" class="w-full md:w-1/2 bg-gray-500 text-white font-bold py-3 px-6 rounded-lg hover:bg-gray-600 transition-colors">再看一次故事</button> | |
| <button id="submit-quiz-button" class="w-full md:w-1/2 bg-green-500 text-white font-bold py-3 px-6 rounded-lg hover:bg-green-600 transition-colors">提交答案</button> | |
| </div> | |
| </div> | |
| <!-- 遊戲畫面 (預設隱藏) --> | |
| <div id="game-view" class="hidden bg-white/80 backdrop-blur-md rounded-xl shadow-2xl p-8 tower-bg"> | |
| <h1 class="text-3xl md:text-4xl font-bold text-gray-800 text-center mb-2">哲學之塔</h1> | |
| <p class="text-center text-gray-600 mb-6">幫助被囚禁的西帕索斯,找出正方形的神秘邊長!</p> | |
| <div class="grid md:grid-cols-2 gap-8 items-center"> | |
| <!-- 左側:正方形展示區 --> | |
| <div class="flex flex-col items-center justify-center bg-white/70 p-6 rounded-lg shadow-inner"> | |
| <div id="square-display" class="w-48 h-48 bg-blue-300 border-4 border-blue-500 flex items-center justify-center relative transition-transform duration-500"> | |
| <p class="text-2xl font-bold text-white">面積 = <span id="target-area-text">2</span></p> | |
| </div> | |
| <p class="mt-4 text-2xl font-mono text-gray-700">邊長 = <span id="target-sqrt-text">√2</span></p> | |
| </div> | |
| <!-- 右側:互動操作區 --> | |
| <div class="flex flex-col space-y-6"> | |
| <div class="bg-white/80 p-4 rounded-lg text-center"> | |
| <p class="text-lg">你的猜測邊長 <span id="guess-sqrt-text" class="font-mono">√2</span> ≒ <span id="current-value" class="font-bold text-2xl text-indigo-600">1.50</span></p> | |
| <p class="text-lg">邊長的平方(正方形面積):<span id="current-squared-value" class="font-bold text-2xl text-red-500">2.25</span></p> | |
| </div> | |
| <input type="range" id="value-slider" min="1" max="2" value="1.5" step="0.01" class="w-full"> | |
| <button id="check-button" class="w-full bg-green-500 text-white font-bold py-3 px-4 rounded-lg hover:bg-green-600 transition-colors shadow-md text-xl"> | |
| 確定答案 | |
| </button> | |
| <div id="feedback-box" class="text-center text-xl font-semibold min-h-[32px]"></div> | |
| <button id="next-level-button" class="w-full bg-indigo-500 text-white font-bold py-3 px-4 rounded-lg hover:bg-indigo-600 transition-colors shadow-md text-xl hidden"> | |
| 挑戰下一關 | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- 生活應用畫面 (預設隱藏) --> | |
| <div id="application-view" class="hidden bg-white/80 backdrop-blur-md rounded-xl shadow-2xl p-8"> | |
| <h1 class="text-3xl md:text-4xl font-bold text-green-600 mb-4 text-center">恭喜你,成功解救了西帕索斯!</h1> | |
| <p class="text-lg text-gray-700 mb-8 text-center">你們玩遊戲的時候,有沒有想過,為什麼角色撞到怪物就會扣血?為什麼子彈會正好打到你?這不是靠神奇的第六感,是靠數學──而且關鍵數學招式就是平方根!</p> | |
| <div class="bg-blue-50 p-6 rounded-lg text-left"> | |
| <h2 class="text-2xl font-bold text-indigo-600 mb-4 text-center">🎮 遊戲物理引擎裡的平方根魔法</h2> | |
| <div class="space-y-6"> | |
| <div> | |
| <h3 class="font-semibold text-xl mb-2">判斷「有沒有撞到」</h3> | |
| <p>遊戲世界是由座標組成的(就像地圖上的 X、Y 點)。當角色和怪物在不同位置,遊戲必須算兩者的距離,來判斷是不是近到可以碰撞。</p> | |
| <p class="mt-2 p-3 bg-gray-200 rounded-md text-center font-mono text-lg">距離 = √((x₁ - x₂)² + (y₁ - y₂)²)</p> | |
| <p class="mt-2">最後那個開根號,就是讓距離變回「真實長度」。沒有平方根,遊戲根本不知道你到底是不是碰到牆、怪物、寶箱。</p> | |
| </div> | |
| <div> | |
| <h3 class="font-semibold text-xl mb-2">算出移動的真速度</h3> | |
| <p>在 3D 遊戲中,速度不只有一個方向。例如你同時向前(X 軸)+ 向右(Y 軸)移動,總速度不是單純相加,而是:</p> | |
| <p class="mt-2 p-3 bg-gray-200 rounded-md text-center font-mono text-lg">總速度 = √(水平速度² + 垂直速度²)</p> | |
| <p class="mt-2">這樣遊戲才能正確決定動畫快慢、物理反應強度。</p> | |
| </div> | |
| <div> | |
| <h3 class="font-semibold text-xl mb-2">模擬「真實的碰撞反應」</h3> | |
| <p>角色被怪物推一下,會往斜方向飛。遊戲會算推力向量的大小(也就是「合力長度」)來決定你飛多遠,這裡也要平方根。</p> | |
| </div> | |
| </div> | |
| <div class="mt-6 pt-6 border-t"> | |
| <p class="text-xl text-center"><span class="font-bold">💡 簡單比喻:</span>「平方根在遊戲引擎裡,就像一把『距離測量尺』,沒有它,遊戲世界就不知道東西有多近、有多快,甚至不會知道你到底撞到沒。」</p> | |
| </div> | |
| </div> | |
| <div class="mt-12 text-center"> | |
| <a href="index.html" class="inline-block w-full md:w-1/2 bg-indigo-600 text-white font-bold py-3 md:py-4 px-6 rounded-lg hover:bg-indigo-700 transition-colors shadow-lg text-lg md:text-xl"> | |
| 回到探險島地圖 | |
| </a> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| document.addEventListener('DOMContentLoaded', () => { | |
| // --- 關卡設定 --- | |
| const levels = [ | |
| { target: 2, min: 1, max: 2, tolerance: 0.05, type: 'manual' }, | |
| { target: 3, min: 1, max: 2, tolerance: 0.05, type: 'auto', speed: 0.007 }, | |
| { target: 5, min: 2, max: 3, tolerance: 0.05, type: 'auto', speed: 0.011 } | |
| ]; | |
| let currentLevel = 0; | |
| const quizQuestions = [ | |
| { answer: 'C' }, | |
| { answer: 'B' }, | |
| { answer: 'D' } | |
| ]; | |
| // --- DOM 元素 --- | |
| const storyView = document.getElementById('story-view'); | |
| const startQuizButton = document.getElementById('start-quiz-button'); | |
| const quizView = document.getElementById('quiz-view'); | |
| const submitQuizButton = document.getElementById('submit-quiz-button'); | |
| const rereadStoryButton = document.getElementById('reread-story-button'); | |
| const quizFeedback = document.getElementById('quiz-feedback'); | |
| const gameView = document.getElementById('game-view'); | |
| const applicationView = document.getElementById('application-view'); | |
| const squareDisplay = document.getElementById('square-display'); | |
| const targetAreaText = document.getElementById('target-area-text'); | |
| const targetSqrtText = document.getElementById('target-sqrt-text'); | |
| const guessSqrtText = document.getElementById('guess-sqrt-text'); | |
| const currentValue = document.getElementById('current-value'); | |
| const currentSquaredValue = document.getElementById('current-squared-value'); | |
| const valueSlider = document.getElementById('value-slider'); | |
| const checkButton = document.getElementById('check-button'); | |
| const feedbackBox = document.getElementById('feedback-box'); | |
| const nextLevelButton = document.getElementById('next-level-button'); | |
| // --- 遊戲狀態 --- | |
| let sliderAnimationId = null; | |
| let sliderDirection = 1; | |
| // --- 核心函式 --- | |
| function initLevel(levelIndex) { | |
| const level = levels[levelIndex]; | |
| if (sliderAnimationId) { | |
| cancelAnimationFrame(sliderAnimationId); | |
| sliderAnimationId = null; | |
| } | |
| targetAreaText.textContent = level.target; | |
| targetSqrtText.innerHTML = `√${level.target}`; | |
| guessSqrtText.innerHTML = `√${level.target}`; | |
| valueSlider.min = level.min; | |
| valueSlider.max = level.max; | |
| valueSlider.value = (level.min + level.max) / 2; | |
| valueSlider.step = 0.01; | |
| updateSliderDisplay(); | |
| feedbackBox.textContent = ''; | |
| feedbackBox.className = 'text-center text-xl font-semibold min-h-[32px]'; | |
| checkButton.disabled = false; | |
| nextLevelButton.classList.add('hidden'); | |
| squareDisplay.classList.remove('success-flash'); | |
| if (level.type === 'manual') { | |
| valueSlider.style.pointerEvents = 'auto'; | |
| checkButton.textContent = '確定答案'; | |
| } else { | |
| valueSlider.style.pointerEvents = 'none'; | |
| checkButton.textContent = '按下鎖定!'; | |
| sliderDirection = 1; | |
| startSliderAnimation(); | |
| } | |
| if (levelIndex === levels.length - 1) { | |
| nextLevelButton.textContent = '查看生活應用'; | |
| } else { | |
| nextLevelButton.textContent = '挑戰下一關'; | |
| } | |
| } | |
| function startSliderAnimation() { | |
| const level = levels[currentLevel]; | |
| let val = parseFloat(valueSlider.value); | |
| val += sliderDirection * level.speed; | |
| if (val >= level.max || val <= level.min) { | |
| sliderDirection *= -1; | |
| val = Math.max(level.min, Math.min(level.max, val)); | |
| } | |
| valueSlider.value = val; | |
| updateSliderDisplay(); | |
| sliderAnimationId = requestAnimationFrame(startSliderAnimation); | |
| } | |
| function updateSliderDisplay() { | |
| const val = parseFloat(valueSlider.value); | |
| const squaredVal = val * val; | |
| currentValue.textContent = val.toFixed(2); | |
| currentSquaredValue.textContent = squaredVal.toFixed(2); | |
| } | |
| function checkAnswer() { | |
| const level = levels[currentLevel]; | |
| if (level.type === 'auto' && sliderAnimationId) { | |
| cancelAnimationFrame(sliderAnimationId); | |
| sliderAnimationId = null; | |
| } | |
| checkButton.disabled = true; | |
| const val = parseFloat(valueSlider.value); | |
| const squaredVal = val * val; | |
| const difference = Math.abs(squaredVal - level.target); | |
| feedbackBox.textContent = ''; | |
| if (difference <= level.tolerance) { | |
| feedbackBox.textContent = '太棒了!你找到了!'; | |
| feedbackBox.className = 'text-center text-xl font-semibold text-green-600'; | |
| nextLevelButton.classList.remove('hidden'); | |
| squareDisplay.classList.add('success-flash'); | |
| } else { | |
| const hint = squaredVal < level.target ? '太小了' : '太大了'; | |
| feedbackBox.textContent = `喔喔!${hint},再試一次!`; | |
| feedbackBox.className = 'text-center text-xl font-semibold text-red-600'; | |
| if (level.type === 'auto') { | |
| setTimeout(() => { | |
| initLevel(currentLevel); | |
| }, 2000); | |
| } else { | |
| checkButton.disabled = false; | |
| } | |
| } | |
| } | |
| function loadNext() { | |
| currentLevel++; | |
| if (currentLevel < levels.length) { | |
| initLevel(currentLevel); | |
| } else { | |
| gameView.classList.add('hidden'); | |
| applicationView.classList.remove('hidden'); | |
| } | |
| } | |
| function checkQuiz() { | |
| let allCorrect = true; | |
| quizFeedback.textContent = ''; | |
| quizFeedback.classList.remove('text-red-500', 'text-green-600'); | |
| quizQuestions.forEach((question, index) => { | |
| const container = document.getElementById(`question-container-${index}`); | |
| const selected = document.querySelector(`input[name="q${index}"]:checked`); | |
| container.classList.remove('incorrect'); | |
| if (!selected || selected.value !== question.answer) { | |
| allCorrect = false; | |
| container.classList.add('incorrect'); | |
| } | |
| }); | |
| if (allCorrect) { | |
| quizFeedback.textContent = '太棒了!你很用心在看故事喔!'; | |
| quizFeedback.classList.add('text-green-600'); | |
| submitQuizButton.disabled = true; | |
| rereadStoryButton.disabled = true; | |
| setTimeout(() => { | |
| quizView.classList.add('hidden'); | |
| gameView.classList.remove('hidden'); | |
| }, 2000); | |
| } else { | |
| quizFeedback.textContent = '有題目答錯了,再檢查看看或重讀一次故事吧!'; | |
| quizFeedback.classList.add('text-red-500'); | |
| } | |
| } | |
| // --- 事件監聽 --- | |
| startQuizButton.addEventListener('click', () => { | |
| storyView.classList.add('hidden'); | |
| quizView.classList.remove('hidden'); | |
| submitQuizButton.disabled = false; | |
| rereadStoryButton.disabled = false; | |
| quizFeedback.textContent = ''; | |
| quizQuestions.forEach((_, index) => { | |
| document.getElementById(`question-container-${index}`).classList.remove('incorrect'); | |
| }); | |
| }); | |
| rereadStoryButton.addEventListener('click', () => { | |
| quizView.classList.add('hidden'); | |
| storyView.classList.remove('hidden'); | |
| }); | |
| submitQuizButton.addEventListener('click', checkQuiz); | |
| valueSlider.addEventListener('input', updateSliderDisplay); | |
| checkButton.addEventListener('click', checkAnswer); | |
| nextLevelButton.addEventListener('click', loadNext); | |
| // --- 初始啟動 --- | |
| initLevel(currentLevel); | |
| }); | |
| </script> | |
| </body> | |
| </html> | |