import os import streamlit as st import google.generativeai as genai import random # --- Streamlit 설정 --- st.set_page_config( page_title="SDGs 정책 결정 게임", page_icon="🌍", layout="wide", initial_sidebar_state="expanded", ) # --- Custom CSS (간단하게) --- st.markdown( """ """, unsafe_allow_html=True, ) # --- API 키 설정 --- if "GEMINI_API_KEY" not in os.environ: st.error( "GEMINI_API_KEY 환경 변수가 설정되지 않았습니다. Hugging Face Secrets 또는 환경 변수에 API 키를 설정해주세요." ) st.stop() genai.configure(api_key=os.environ["GEMINI_API_KEY"]) # --- Gemini 모델 설정 --- generation_config = { "temperature": 1.0, "top_p": 0.95, "top_k": 64, "max_output_tokens": 5000, # 줄이기 } model = genai.GenerativeModel( model_name="gemini-2.0-flash-exp", generation_config=generation_config, ) # --- 세션 상태 초기화 --- if "sdgs_chat_session" not in st.session_state: st.session_state["sdgs_chat_session"] = model.start_chat(history=[]) if "current_sdg_scenario" not in st.session_state: st.session_state["current_sdg_scenario"] = None if "policy_options" not in st.session_state: st.session_state["policy_options"] = None if "policy_result" not in st.session_state: st.session_state["policy_result"] = None if "selected_sdg" not in st.session_state: st.session_state["selected_sdg"] = None if "user_policy_option" not in st.session_state: st.session_state["user_policy_option"] = "" if "points" not in st.session_state: st.session_state["points"] = 0 if "level" not in st.session_state: st.session_state["level"] = 1 # --- SDGs 목표 목록 및 설명 --- sdg_goals = { "SDG 1": "빈곤 퇴치", "SDG 2": "기아 종식", "SDG 3": "건강과 웰빙", "SDG 4": "양질의 교육", "SDG 5": "성평등", "SDG 6": "깨끗한 물과 위생", "SDG 7": "깨끗한 에너지", "SDG 8": "양질의 일자리와 경제 성장", "SDG 9": "산업, 혁신, 사회 기반 시설", "SDG 10": "불평등 완화", "SDG 11": "지속가능한 도시와 공동체", "SDG 12": "지속가능한 소비, 생산", "SDG 13": "기후 변화 대응", "SDG 14": "해양 생태계 보전", "SDG 15": "육상 생태계 보전", "SDG 16": "평화, 정의, 제도", "SDG 17": "파트너십", } sdg_descriptions = { "SDG 1": "모든 곳에서, 모든 형태의 빈곤을 끝내는 것", "SDG 2": "기아를 없애고, 식량 안보를 이루며, 영양 상태를 개선하고, 지속가능한 농업을 발전시키는 것", "SDG 3": "모든 사람의 건강과 웰빙을 증진시키는 것", "SDG 4": "모두에게 질 좋은 교육을 보장하고, 평생 학습 기회를 늘리는 것", "SDG 5": "성평등을 이루고, 모든 여성과 소녀의 권한을 강화하는 것", "SDG 6": "모두가 깨끗한 물과 위생 시설을 이용할 수 있도록 하는 것", "SDG 7": "모두에게 저렴하고 깨끗한 에너지를 보장하는 것", "SDG 8": "모두를 위한 지속적이고 포용적이며 지속가능한 경제 성장, 생산적인 완전 고용과 양질의 일자리를 증진시키는 것", "SDG 9": "튼튼한 사회 기반 시설을 건설하고, 포용적이고 지속가능한 산업화를 증진하며, 혁신을 장려하는 것", "SDG 10": "국가 내 그리고 국가 간 불평등을 줄이는 것", "SDG 11": "안전하고 회복력 있고 지속가능한 도시와 인간 거주지를 만드는 것", "SDG 12": "지속가능한 소비와 생산 패턴을 확립하는 것", "SDG 13": "기후 변화와 그 영향에 맞서 싸우기 위한 긴급 조치를 취하는 것", "SDG 14": "지속가능한 발전을 위해 해양과 해양 자원을 보존하고 지속가능하게 사용하는 것", "SDG 15": "육지 생태계를 보호하고 복원하며 지속가능하게 사용하고, 산림을 지속가능하게 경영하며, 사막화를 방지하고, 토지 황폐화를 중지시키고 되살리며, 생물 다양성 손실을 멈추게 하는 것", "SDG 16": "지속가능한 발전을 위해 평화롭고 포용적인 사회를 증진하고, 모두에게 정의를 보장하며, 모든 수준에서 효과적이고 책임감 있고 포용적인 제도를 구축하는 것", "SDG 17": "지속가능한 발전을 위한 이행 수단을 강화하고, 글로벌 파트너십을 활성화하는 것", } # --- 레벨업 기준 포인트 --- level_points = { 2: 20, 3: 50, 4: 100, 5: 200, } # --- 기본 포인트 부여 기준 (정책 결정 외 행동) --- base_points_per_action = { "scenario": 5, "options": 3, "retry": 1, } # --- 정책 평가 키워드 --- positive_keywords = ["성공", "개선", "향상", "긍정적", "효과적", "증진", "발전", "도움", "기여", "해결"] negative_keywords = ["어려움", "문제", "악화", "부정적", "비효율적", "부족", "손실", "악영향", "한계", "실패"] point_ranges = { # 정책 평가에 따른 포인트 범위 "positive": (10, 15), "neutral": (5, 10), "negative": (1, 5), } # --- 레벨 업데이트 함수 --- def update_level(): current_level = st.session_state["level"] current_points = st.session_state["points"] next_level = current_level + 1 if next_level in level_points and current_points >= level_points[next_level]: st.session_state["level"] = next_level st.success(f"🎉 레벨 업! Level {next_level} 달성! 🎉") st.toast(f"🎉 레벨 업! Level {next_level} 달성! 🎉", icon="🚀") # --- 시나리오 생성 함수 --- def generate_sdg_scenario(sdg_goal_name, sdg_goal_text): prompt = f""" 지시: 초등학생 6학년 수준에 맞춰서, {sdg_goal_text}({sdg_goal_name}) 목표와 관련된 정책 결정 시뮬레이션 시나리오를 생성해주세요. 시나리오는 8~10문장 정도로 현실적인 사회 문제 상황을 설명하고, 학생들이 정책 결정의 필요성을 느낄 수 있도록 작성해주세요. "## 시나리오" 로 시작해주세요. **생성된 시나리오:** """ chat_session = st.session_state["sdgs_chat_session"] response = chat_session.send_message(prompt) scenario_text = response.text.strip() if scenario_text.startswith("## 시나리오"): scenario_content = scenario_text[len("## 시나리오") :].strip() st.session_state["points"] += base_points_per_action["scenario"] # 기본 포인트 증가 update_level() # 레벨 업데이트 return scenario_content return scenario_text # 혹시라도 "## 시나리오" 로 시작 안하면 그냥 반환 # --- 정책 옵션 생성 함수 --- def generate_policy_options(scenario_text): prompt = f""" **시나리오:** {scenario_text} **지시:** 위 시나리오에 제시된 문제 상황을 해결하기 위한 정책 옵션 3가지 정도를 초등학생 6학년 수준에 맞춰서 생성해주세요. 각 옵션은 2~3문장으로 간결하게 설명해주세요. 옵션은 번호와 함께 "옵션 [번호]: " 로 시작해주세요. (예: 옵션 1: , 옵션 2: ...) **생성된 정책 옵션:** """ chat_session = st.session_state["sdgs_chat_session"] response = chat_session.send_message(prompt) options_text = response.text.strip() policy_options_list = {} if options_text: options_lines = [line.strip() for line in options_text.split('\n') if line.strip()] for line in options_lines: if line.startswith("옵션 "): parts = line.split(":", 1) # 첫 번째 ":" 기준으로 분리 if len(parts) == 2: option_number = parts[0].split(" ")[1] # "옵션 1" 에서 "1" 추출 option_description = parts[1].strip() policy_options_list[option_number] = option_description st.session_state["points"] += base_points_per_action["options"] # 기본 포인트 증가 update_level() # 레벨 업데이트 return policy_options_list # --- 정책 결과 생성 및 평가 함수 --- def generate_policy_result(scenario_text, selected_option_text): prompt = f""" **시나리오:** {scenario_text} **선택한 정책 옵션:** {selected_option_text} **지시:** 위 시나리오 문제 상황에서 선택한 정책 옵션을 실행했을 때 예상되는 결과를 초등학생 6학년 수준에 맞춰서 6~8문장 정도로 설명해주세요. 정책의 긍정적인 효과와 함께 예상되는 어려움이나 부작용도 **솔직하게** 함께 설명해주세요. 정책 결과가 긍정적인지, 중립적인지, 부정적인지 스스로 평가하여 함께 알려주세요. (예: 긍정적 결과, 중립적 결과, 부정적 결과) "## 정책 결과" 로 시작하고, 그 다음에 정책 평가(긍정적 결과, 중립적 결과, 부정적 결과 중 하나)를 괄호 안에 넣어서 표시해주세요. (예: ## 정책 결과 (긍정적 결과)) 그리고 정책 결과 텍스트를 이어서 작성해주세요. **생성된 정책 결과:** """ chat_session = st.session_state["sdgs_chat_session"] response = chat_session.send_message(prompt) result_text = response.text.strip() policy_result_text = "" policy_evaluation = "neutral" # 기본 평가: 중립 if result_text.startswith("## 정책 결과"): result_content_with_eval = result_text[len("## 정책 결과") :].strip() if result_content_with_eval.startswith("(긍정적 결과)"): policy_result_text = result_content_with_eval[len("(긍정적 결과)") :].strip() policy_evaluation = "positive" elif result_content_with_eval.startswith("(부정적 결과)"): policy_result_text = result_content_with_eval[len("(부정적 결과)") :].strip() policy_evaluation = "negative" elif result_content_with_eval.startswith("(중립적 결과)"): policy_result_text = result_content_with_eval[len("(중립적 결과)") :].strip() policy_evaluation = "neutral" else: policy_result_text = result_content_with_eval.strip() # 평가 정보 없을 경우 # 정책 평가 기반 포인트 부여 if policy_evaluation == "positive": points = random.randint(*point_ranges["positive"]) elif policy_evaluation == "negative": points = random.randint(*point_ranges["negative"]) else: # neutral points = random.randint(*point_ranges["neutral"]) st.session_state["points"] += points # 정책 평가 기반 포인트 증가 update_level() # 레벨 업데이트 return policy_result_text, policy_evaluation, points # --- 메인 화면 --- def main(): st.sidebar.header("🌍 SDGs 정책 결정 게임") st.sidebar.markdown(f"### 🏆 Level {st.session_state['level']}") # 레벨 표시 st.sidebar.markdown(f"### ⭐ 포인트: {st.session_state['points']}") # 포인트 표시 st.sidebar.markdown("---") selected_sdg_name = st.sidebar.selectbox("SDGs 목표 선택:", list(sdg_goals.keys())) sdg_goal_text = sdg_goals[selected_sdg_name] if st.session_state["selected_sdg"] != selected_sdg_name: # 목표가 바뀌면 시나리오, 옵션, 결과 초기화 st.session_state["current_sdg_scenario"] = None st.session_state["policy_options"] = None st.session_state["policy_result"] = None st.session_state["selected_sdg"] = selected_sdg_name st.session_state["user_policy_option"] = "" # 사용자 입력 옵션 초기화 with st.sidebar.expander("📖 SDGs 목표 설명"): for goal_name, goal_text in sdg_goals.items(): st.markdown(f"**{goal_name}: {goal_text}**") st.write(sdg_descriptions[goal_name]) st.markdown("---") with st.sidebar.expander("🚀 앱 사용 가이드"): st.markdown( """ **SDGs 정책 결정 게임 방법** 1. **SDGs 목표 선택:** 왼쪽 사이드바에서 정책 결정을 해보고 싶은 SDGs 목표를 선택하세요. 2. **시나리오 생성:** '시나리오 생성' 버튼을 클릭하여 선택한 목표와 관련된 문제 상황 시나리오를 받아보세요. (⭐ **기본 포인트 +5**) 3. **정책 옵션 생성:** 시나리오를 읽고 '정책 옵션 생성' 버튼을 클릭하여 AI가 제시하는 정책 옵션들을 확인하세요. (⭐ **기본 포인트 +3**) 4. **정책 옵션 선택 또는 직접 입력:** * **AI 옵션 선택:** 제시된 옵션 중 하나를 선택하세요. * **직접 입력:** '직접 입력'을 선택하고, 아래 텍스트 입력 창에 여러분이 생각하는 정책 아이디어를 직접 적어보세요. 5. **정책 결정 실행:** 옵션을 선택하거나 직접 입력한 후 '정책 결정 실행' 버튼을 클릭하세요. (⭐ **정책 평가 기반 포인트**) 6. **정책 결과 확인:** 선택한 정책의 예상 결과와 함께 획득한 포인트를 확인하세요. 정책 결과가 좋을수록 더 많은 포인트를 얻을 수 있습니다. 7. **새로운 시나리오:** '새로운 시나리오로 다시하기' 버튼을 클릭하면 새로운 문제 상황으로 다시 게임을 시작할 수 있습니다. (⭐ **재도전 기본 포인트 +1**) **포인트와 레벨:** * 게임을 진행하면서 포인트를 얻고, 레벨을 올릴 수 있습니다. * **정책 결과가 좋을수록 더 많은 포인트를 획득**하여 레벨을 빠르게 올릴 수 있습니다. * 다양한 SDGs 목표에 도전하고, 좋은 정책 결정을 많이 내려서 높은 레벨에 도전해보세요! **팁:** * 각 SDGs 목표가 무엇을 의미하는지 'SDGs 목표 설명'을 참고하세요. * 시나리오를 꼼꼼히 읽고, 문제의 원인과 영향을 파악하는 것이 중요합니다. * 정책 옵션의 긍정적 효과와 예상되는 어려움을 고려하여 신중하게 선택하세요. * **더 나은 정책을 선택하거나, 창의적인 정책 아이디어를 직접 입력**하여 높은 점수를 획득해보세요! **재미있게 SDGs 정책 결정 게임을 즐기면서 지속가능한 사회를 위한 문제 해결 능력을 키워보세요!** """ ) st.title(f"🎯 {selected_sdg_name}: {sdg_goal_text} 정책 결정 시뮬레이션") if not st.session_state["current_sdg_scenario"]: if st.button("시나리오 생성", use_container_width=True): with st.spinner(f"{selected_sdg_name} 시나리오 생성 중..."): scenario = generate_sdg_scenario(selected_sdg_name, sdg_goal_text) st.session_state["current_sdg_scenario"] = scenario st.session_state["policy_options"] = None # 시나리오 새로 생성시 옵션 초기화 st.session_state["policy_result"] = None # 시나리오 새로 생성시 결과 초기화 st.session_state["user_policy_option"] = "" # 시나리오 새로 생성시 사용자 입력 옵션 초기화 st.rerun() # rerun to display scenario immediately else: st.subheader("📜 시나리오") st.info(st.session_state["current_sdg_scenario"]) if st.session_state["current_sdg_scenario"] and not st.session_state["policy_options"]: if st.button("정책 옵션 생성", use_container_width=True): with st.spinner("정책 옵션 생성 중..."): policy_options = generate_policy_options(st.session_state["current_sdg_scenario"]) st.session_state["policy_options"] = policy_options st.session_state["policy_result"] = None # 옵션 새로 생성시 결과 초기화 st.session_state["user_policy_option"] = "" # 옵션 새로 생성시 사용자 입력 옵션 초기화 st.rerun() # rerun to display options immediately if st.session_state["policy_options"]: st.subheader("⚙️ 정책 옵션 선택") policy_option_keys = list(st.session_state["policy_options"].keys()) policy_option_keys_with_input = ["직접 입력"] + policy_option_keys # "직접 입력" 옵션 추가 selected_option_key = st.radio("정책 옵션을 선택하세요:", policy_option_keys_with_input) if selected_option_key == "직접 입력": # "직접 입력" 선택 시 텍스트 입력 창 표시 user_input_option = st.text_area("직접 정책 옵션 입력:", value=st.session_state["user_policy_option"]) st.session_state["user_policy_option"] = user_input_option # 세션 상태 업데이트 selected_option_text = user_input_option # 정책 결과 생성 시 사용자 입력 옵션 사용 else: selected_option_text = st.session_state["policy_options"][selected_option_key] # AI 생성 옵션 사용 st.info(f"**선택한 옵션 {selected_option_key}:** {selected_option_text}") # AI 생성 옵션 정보 표시 if st.button("정책 결정 실행", use_container_width=True): with st.spinner("정책 결과 분석 중..."): policy_result_text, policy_evaluation, points = generate_policy_result(st.session_state["current_sdg_scenario"], selected_option_text) st.session_state["policy_result"] = (policy_result_text, policy_evaluation, points) # 결과 튜플로 저장 st.rerun() # rerun to display result immediately if st.session_state["policy_result"]: policy_result_text, policy_evaluation, points = st.session_state["policy_result"] # 결과 튜플에서 값 추출 st.subheader("💡 정책 결과") if policy_evaluation == "positive": st.success(f"✅ {policy_result_text}") elif policy_evaluation == "negative": st.warning(f"⚠️ {policy_result_text}") else: # neutral st.info(f"😐 {policy_result_text}") st.markdown(f"획득 포인트: ⭐ **{points}** points ({policy_evaluation} 정책)") # 획득 포인트 표시 if st.button("새로운 시나리오로 다시하기", use_container_width=True): st.session_state["current_sdg_scenario"] = None st.session_state["policy_options"] = None st.session_state["policy_result"] = None st.session_state["user_policy_option"] = "" # 결과 확인 후 사용자 입력 옵션 초기화 st.session_state["points"] += base_points_per_action["retry"] # 재도전 기본 포인트 증가 update_level() # 레벨 업데이트 st.rerun() if __name__ == "__main__": main()