AR04301 / app.py
Joey889's picture
Update app.py
868e5e4 verified
import gradio as gr
import math
treasures = [
{"name": "寶藏第1個", "lat": 24.985703, "lon": 121.288680, "question": "請問喬一老師叫什麼課?", "answer": "STEAM"},
{"name": "寶藏第2個", "lat": 24.985900, "lon": 121.289000, "question": "請問今年是何年?", "answer": "2025"},
{"name": "寶藏第3個", "lat": 24.956398, "lon": 121.297729, "question": "請問熊熊老師叫什麼課?", "answer": "balloon"},
]
def haversine(lat1, lon1, lat2, lon2):
R = 6371000
phi1, phi2 = math.radians(lat1), math.radians(lat2)
d_phi = math.radians(lat2 - lat1)
d_lambda = math.radians(lon2 - lon1)
a = math.sin(d_phi/2)**2 + math.cos(phi1)*math.cos(phi2)*math.sin(d_lambda/2)**2
c = 2 * math.atan2(math.sqrt(a), math.sqrt(1-a))
return R * c
def find_nearest(lat, lon, found):
nearest = None
nearest_idx = None
min_distance = float("inf")
for idx, treasure in enumerate(treasures):
if idx in found:
continue
dist = haversine(lat, lon, treasure["lat"], treasure["lon"])
if dist < min_distance:
min_distance = dist
nearest = treasure
nearest_idx = idx
return nearest, nearest_idx, min_distance
def update_position(lat, lon, found):
if not lat or not lon:
return "❌ 請先取得 GPS", gr.update(visible=False), "", "", found
try:
lat = float(lat)
lon = float(lon)
except:
return "❌ 座標格式錯誤", gr.update(visible=False), "", "", found
nearest, nearest_idx, dist = find_nearest(lat, lon, found)
if nearest is None:
return "🎉 所有寶藏已完成!", gr.update(visible=False), "", "", found
if dist <= 200:
return f"📍 距離 {nearest['name']}{dist:.2f} 公尺 ✅ 請回答問題:", gr.update(visible=True), nearest["question"], nearest_idx, found
else:
return f"📍 距離 {nearest['name']}{dist:.2f} 公尺,請再靠近一些", gr.update(visible=False), "", "", found
def check_answer(user_answer, nearest_idx, found):
correct = treasures[nearest_idx]["answer"]
if user_answer.strip().lower() == correct.strip().lower():
return "✅ 答對了!請上傳一張照片作為紀念!", gr.update(visible=True), nearest_idx, found
else:
return "❌ 答錯了,請再試一次。", gr.update(visible=False), None, found
def complete_mission(photo, nearest_idx, found):
if photo is not None:
found.append(nearest_idx)
if len(found) == len(treasures):
return "🎉 所有寶藏完成!", """
<h2 style='text-align:center;'>🎉 恭喜完成全部探索任務!</h2>
<img src="https://media.giphy.com/media/v1.Y2lkPTc5MGI3NjExZGU4YzZhOTdjYjJmODlmZDEwZmExZDcyOTU4NGY5NGY4MzU3ZDM2YSZjdD1n/26n6WywJyh39n1pBu/giphy.gif" style="width:100%; max-height:300px;">
""", found
else:
return "✅ 照片上傳成功!繼續尋找下一個寶藏~", "", found
else:
return "❌ 請上傳照片!", "", found
with gr.Blocks() as demo:
gr.Markdown("# 🗺️ GPS 探索互動尋寶遊戲")
gr.Markdown("📍 自動定位,靠近寶藏觸發題目任務")
with gr.Row():
lat = gr.Textbox(label="Latitude")
lon = gr.Textbox(label="Longitude")
found_state = gr.State([])
status = gr.Markdown()
question_box = gr.Textbox(label="回答問題", visible=False)
question_text = gr.Textbox(visible=False)
nearest_idx_state = gr.State()
photo_upload = gr.Image(type="filepath", label="上傳紀念照片", visible=False)
final_message = gr.Markdown()
final_celebration = gr.HTML()
btn_check = gr.Button("🔍 刷新位置")
btn_check.click(update_position, inputs=[lat, lon, found_state],
outputs=[status, question_box, question_text, nearest_idx_state, found_state])
btn_submit_answer = gr.Button("✅ 提交回答")
btn_submit_answer.click(check_answer, inputs=[question_box, nearest_idx_state, found_state],
outputs=[status, photo_upload, nearest_idx_state, found_state])
btn_upload_photo = gr.Button("📸 上傳完成")
btn_upload_photo.click(complete_mission, inputs=[photo_upload, nearest_idx_state, found_state],
outputs=[status, final_celebration, found_state])
gr.HTML("""
<script>
setInterval(() => {
navigator.geolocation.getCurrentPosition(
function(pos) {
document.querySelectorAll('textarea')[0].value = pos.coords.latitude;
document.querySelectorAll('textarea')[1].value = pos.coords.longitude;
document.querySelectorAll('textarea')[0].dispatchEvent(new Event('input'));
document.querySelectorAll('textarea')[1].dispatchEvent(new Event('input'));
document.querySelector('button[id$="btn_check"]').click();
}
);
}, 5000);
</script>
""")
demo.launch()