ll7098ll commited on
Commit
f3edf46
·
verified ·
1 Parent(s): 51a50b0

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +453 -268
app.py CHANGED
@@ -1,326 +1,511 @@
1
  import streamlit as st
2
  import random
3
- from openai import OpenAI
4
  import time
5
 
6
- # --- OpenAI 클라이언트 설정 (API 키 필요) ---
7
- # API 설정 확인 (환경 변수 또는 secrets 사용 권장)
8
- # 예: client = OpenAI(api_key=st.secrets["OPENAI_API_KEY"])
9
- # 예제에서는 API 호출 부분을 주석 처리하거나 임시 데이터로 대체합니다.
10
- # client = OpenAI(...) # 실제 사용 시 주석 해제 및 키 설정
 
 
 
 
 
 
 
 
 
 
 
 
11
 
12
- # --- 국가 기관 초기화 함수 ---
13
- # 이 함수를 세션 상태 초기화보다 먼저 정의합니다.
14
- def initialize_branches():
15
- """국가 기관의 초기 상태와 정보를 설정합니다."""
16
  return {
17
- '행정부': {'name': '정부 (행정부)', 'role': '법률을 집행하고 국가 정책을 수립, 시행합니다.', 'status': '일상 업무 수행 중', 'actions': [], 'power_level': 75},
18
- '국회': {'name': '국회 (입법부)', 'role': '법률을 만들거나 고치고, 국가 예산을 심의하며 행정부를 견제합니다.', 'status': '정기 국회 회기 중', 'actions': [], 'power_level': 80},
19
- '법원': {'name': '법원/헌법재판소 (사법부)', 'role': '법률을 해석하고 적용하여 분쟁을 해결하며, 법률의 위헌 여부를 심판합니다.', 'status': '재판 진행 중', 'actions': [], 'power_level': 70}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
  }
21
 
22
  # --- 세션 상태 초기화 ---
23
- # 이제 initialize_branches 함수가 정의되었으므로 호출 가능합니다.
24
- if 'game_year' not in st.session_state:
25
- st.session_state['game_year'] = 2024
26
- if 'national_status' not in st.session_state:
27
- st.session_state['national_status'] = {'stability': 100, 'public_satisfaction': 70, 'influence_points': 5}
28
- # initialize_branches() 호출이 함수 정의 뒤에 오도록 수정
29
- if 'government_branches' not in st.session_state:
30
- st.session_state['government_branches'] = initialize_branches()
31
- if 'current_issue' not in st.session_state:
32
- st.session_state['current_issue'] = None
33
- if 'issue_analysis' not in st.session_state:
34
- st.session_state['issue_analysis'] = None
35
- if 'event_log' not in st.session_state:
36
- st.session_state['event_log'] = []
37
-
38
-
39
- # --- 이슈 생성 함수 (generate_news 대체) ---
40
- def generate_issue(game_year):
41
- # ... (이하 함수 내용은 이전과 동일)
42
- prompt = f"""
43
- {game_year}년 대한민국에서 발생할 만한 사회적, 정치적, 또는 경제적 이슈를 하나 생성해주세요.
44
- 이슈는 행정부, 입법부, 사법부 하나 이상의 기관이 반응해야 성격을 가져야 합니다.
45
- 이슈 내용은 3~4문장으로 간결하게 설명해주세요.
46
- 이슈 제목은 "## 이슈: [이슈 제목]" 형식으로 시작해주세요.
47
-
48
- 예시:
49
- ## 이슈: 신종 감염병 국내 유입 확인
50
- 최근 해외에서 유행하던 신종 감염병 X의 국내 첫 확진자가 발생했습니다. 빠른 확산 속도와 높은 치명률로 인해 국민들의 불안감이 커지고 있으며, 방역 당국의 초기 대응 능력에 대한 우려의 목소리가 높습니다. 효과적인 방역 대책 마련과 함께 관련 의료 시스템 정비가 시급한 상황입니다.
51
-
52
- 생성된 이슈:
53
- """
54
- try:
55
- # response = client.chat.completions.create(...) # GPT 호출 (실제 사용 시 주석 해제)
56
- # issue_text = response.choices[0].message.content.strip()
57
- # --- 임시 예시 ---
58
- issues = [
59
- "## 이슈: 최저임금 인상 관련 노사 갈등 심화\n내년도 최저임금 결정을 앞두고 노동계와 경영계의 입장이 첨예하게 대립하고 있습니다. 노동계는 생활 안정을 위해 큰 폭의 인상을 요구하는 반면, 경영계는 경제 상황 악화를 이유로 동결 또는 소폭 인상을 주장하며 갈등이 깊어지고 있습니다. 정부의 중재 역할과 국회의 관련 논의에 관심이 집중되고 있습니다.",
60
- "## 이슈: 인공지능(AI) 창작물의 저작권 논란\n최근 AI 기술이 발전하면서 AI가 생성한 그림, 음악, 글 등의 저작권 귀속 문제가 사회적 논란으로 떠올랐습니다. 현행법으로는 AI 창작물의 권리 관계가 불분명하여 관련 분쟁이 증가하고 있으며, 이에 대한 명확한 법적 기준 마련이 시급하다는 지적이 제기되고 있습니다.",
61
- "## 이슈: 기후 변화로 인한 농작물 피해 급증\n이상 기후 현상이 잦아지면서 특정 지역의 농작물 피해가 심각한 수준에 이르렀습니다. 농가 소득 감소와 식량 안보에 대한 우려가 커지면서, 정부의 실질적인 피해보상 대책과 기후 변화 적응을 위한 장기적인 농업 정책 마련을 요구하는 목소리가 높아지고 있습니다."
62
- ]
63
- issue_text = random.choice(issues)
64
- # --- 임시 예시 끝 ---
65
- if 'event_log' in st.session_state: # 로그 기록 전 확인
66
- st.session_state['event_log'].append(f"{game_year}년: {issue_text.splitlines()[0]}")
67
- return issue_text
68
- except Exception as e:
69
- st.error(f"이슈 생성 오류: {e}")
70
- return None
71
-
72
- # --- 이슈 분석 함수 (explain_daily_news_meanings 대체) ---
73
- def analyze_issue_impact(issue_text):
74
- # ... (이하 함수 내용은 이전과 동일)
75
- if not issue_text: return None
76
- prompt = f"""
77
- 다음 이슈에 대해 대한민국 국가기관(행정부, 입법부, 사법부)이 어떤 역할과 권한에 근거하여 반응할 가능성이 높은지, 그리고 기관 간 어떤 상호작용(견제와 균형)이 예상되는지 간략하게 분석해주세요. 각 기관별 예상 반응과 상호작용 가능성을 명확히 구분하여 설명해주세요.
78
-
79
- 이슈 내용:
80
- {issue_text}
81
-
82
- 분석 결과:
83
- """
84
- try:
85
- # response = client.chat.completions.create(...) # GPT 호출 (실제 사용 시 주석 해제)
86
- # analysis_text = response.choices[0].message.content.strip()
87
- # --- 임시 예시 ---
88
- analysis_text = f"""
89
- **이슈 분석:** "{issue_text.splitlines()[0][len("## 이슈: "):]}"
90
-
91
- **행정부 (정부):**
92
- * **예상 반응:** {random.choice(['관련 부처 중심으로 TF팀 구성 및 현황 파악', '긴급 대책 발표 및 시행령 개정 검토', '피해 지원 예산 편성 및 집행 계획 수립'])} (법률 집행 및 정책 수립 권한)
93
- * **상호작용:** 국회에 관련 법률안/예산안 제출 협의, 법원의 정책 위법성 판단에 대응.
94
-
95
- **입법부 (국회):**
96
- * **예상 반응:** {random.choice(['관련 상임위원회 개최 현안 보고 청취', '긴급 현안 질의 및 대정부 질문', '관련 법률 제정 또는 개정안 발의 및 심의'])} (입법권, 국정 통제권)
97
- * **상호작용:** 행정부가 제출한 법률안/예산안 심의 및 수정, 행정부 정책 집행 과정 감독(국정감사 등), 사법부의 위헌법률심판 결과 주시.
98
-
99
- **사법부 (법원/헌법재판소):**
100
- * **예상 반응:** {random.choice(['이슈 관련 소송 제기 시 재판 진행', '정부 정책/명령의 위법성 심사 가능성', '국회 제정 법률의 위헌 여부 심판 가능성'])} (사법권, 위헌법률심판권)
101
- * **상호작용:** 행정부의 처분/명령에 대한 행정소송 심리, 국회가 제정한 법률에 대한 위헌법률심판 또는 헌법소원 심리.
102
- """
103
- # --- 임시 예시 ---
104
- if 'event_log' in st.session_state: # 로그 기록 확인
105
- st.session_state['event_log'].append(f" - AI 분석 완료")
106
- return analysis_text
107
- except Exception as e:
108
- st.error(f"이슈 분석 중 오류: {e}")
109
- return None
110
-
111
- # --- 플레이어 행동 처리 함수 (buy/sell 대체) ---
112
- def take_player_action(action_type):
113
- # ... (이하 함수 내용은 이전과 동일)
114
- points_cost = 1 # 행동별 포인트 소모량 차등화 가능
115
- if st.session_state['national_status']['influence_points'] >= points_cost:
116
- st.session_state['national_status']['influence_points'] -= points_cost
117
- if 'event_log' in st.session_state: # 로그 기록 전 확인
118
- st.session_state['event_log'].append(f" - 플레이어 행동: {action_type}")
119
- st.success(f"'{action_type}' 행동을 수행했습니다. (영향력 포인트 {points_cost} 소모)")
120
- # 실제 게임에서는 이 행동이 simulate_branch_interactions에 영향을 주도록 구현
121
- return True
122
- else:
123
- st.error("영향력 포인트가 부족합니다.")
 
 
 
 
 
 
 
124
  return False
125
 
126
- # --- 기관 상호작용 및 상태 업데이트 함수 (update_stock_prices 대체) ---
127
- def simulate_branch_interactions():
128
- # ... (이하 함수 내용은 이전과 동일)
129
- # 현재 이슈, 기관 상태, 플레이어 행동 등을 바탕으로
130
- # 기관 간 상호작용(협력, 갈등, 견제) 시뮬레이션
131
- # 예: 국회 법안 통과 -> 행정부 검토 -> (확률적) 재의 요구 -> 국회 재의결 시도 등
132
- # 결과에 따라 national_status (안정도, 만족도 등) 업데이트
133
- stability_change = random.randint(-5, 3) # 임시 변동치
134
- satisfaction_change = random.randint(-4, 4) # 임시 변동치
135
- st.session_state['national_status']['stability'] += stability_change
136
- st.session_state['national_status']['public_satisfaction'] += satisfaction_change
137
- # 안정도, 만족도 0~100 범위 유지
138
- st.session_state['national_status']['stability'] = max(0, min(100, st.session_state['national_status']['stability']))
139
- st.session_state['national_status']['public_satisfaction'] = max(0, min(100, st.session_state['national_status']['public_satisfaction']))
140
-
141
- # 기관 상태도 업데이트 (예: '법안 통과', '정책 시행 중' 등)
142
- for branch in st.session_state['government_branches']:
143
- st.session_state['government_branches'][branch]['status'] = random.choice(['다음 단계 진행 중', '이슈 관련 논의 중', '결정 대기 중'])
144
-
145
- if 'event_log' in st.session_state: # 로그 기록 확인
146
- st.session_state['event_log'].append(f" - 기관 상호작용 결���: 안정도 {stability_change:+} / 만족도 {satisfaction_change:+}")
147
- st.info(f"{st.session_state['game_year']}년 활동 결과가 국가 지표에 반영되었습니다.")
148
- st.toast("국가 지표가 변동되었습니다.", icon="📊")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
149
 
150
 
151
  # --- UI 표시 함수들 ---
152
- def display_national_dashboard():
153
- # ... (이하 함수 내용은 이전과 동일)
154
- st.metric("국가 안정도", f"{st.session_state['national_status']['stability']} / 100")
155
- st.metric("국민 만족도", f"{st.session_state['national_status']['public_satisfaction']} / 100")
156
- st.metric("나의 영향력", f"{st.session_state['national_status']['influence_points']} P")
157
-
158
- def display_branch_status():
159
- # ... (이하 함수 내용은 이전과 동일)
160
- for branch_name, branch_info in st.session_state['government_branches'].items():
161
- with st.expander(f"{branch_info['name']} (현재 상태: {branch_info['status']})"):
162
- st.markdown(f"**주요 역할:** {branch_info['role']}")
163
- # 여기에 더 상세한 활동 내역, 관련 법안/정책 등 표시 가능
164
-
165
- def display_civics_glossary():
166
- # ... (이하 함수 내용은 이전과 동일)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
167
  glossary = {
168
- "삼권분립": "국가 권력을 입법권(국회), 행정권(정부), 사법권(법원)으로 나누어 서로 견제하고 균형을 이루도록 하는 원리.",
169
- "입법권": "법률을 만들거나 고치는 권한. 국회가 가집니다.",
170
- "행정권": "법률을 집행하고 국가 살림을 운영하는 권한. 정부(대통령과 부처)가 가집니다.",
171
- "사법권": "법률을 해석하고 적용하여 재판하는 권한. 법원이 가집니다.",
172
- "견제와 균형": "어느 국가기관이 권력을 독점하지 못하도록 다른 기관이 감시하고 제동을 거는 것.",
173
- "법률안 거부권 (재의 요구권)": "국회가 만든 법률안에 대해 대통령이 동의하지 않고 다시 논의해달라고 국회에 돌려보내는 권한.",
174
- "위헌법률심판": "국회가 만든 법률이 헌법에 어긋나는지(위헌인지) 헌법재판소가 심판하는 것.",
175
- "국정감사": "국회가 정부가 국가 살림을 제대로 하고 있는지 감시하고 조사하는 활동.",
176
- "탄핵 소추": "대통령 고위 공직자가 직무 수행 헌법이나 법률을 크게 위반했을 국회가 공직자를 파면해달라고 헌법재판소에 청구하는 것.",
177
- # 필요에 따라 용어 추가
178
  }
179
- with st.sidebar.expander("🏛️ 시민 용어 사전", expanded=False):
180
  for term, definition in glossary.items():
181
  st.markdown(f"**{term}:** {definition}")
182
  st.markdown("---")
183
 
184
  # --- 메인 게임 루프 ---
185
  def main():
186
- st.title("⚖️ 대한민국 균형 잡기: 삼권분립 시뮬레이션")
187
-
188
- col_issue, col_main_ui = st.columns([2, 3])
189
-
190
- with col_issue:
191
- st.header(f"📰 {st.session_state['game_year']}년 주요 이슈")
192
- if st.button("새로운 이슈 발생시키기", key="issue_gen_button", use_container_width=True):
193
- with st.spinner("새로운 국가 이슈 생성 중..."):
194
- issue = generate_issue(st.session_state['game_year'])
195
- if issue:
196
- st.session_state['current_issue'] = issue
197
- # 이슈 생성 바로 분석 실행
198
- st.session_state['issue_analysis'] = analyze_issue_impact(issue)
199
- st.rerun() # 이슈와 분석 결과를 즉시 화면에 표시하기 위해 새로고침
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
200
  else:
201
- st.error("이슈 생성에 실패했습니다.")
202
 
203
- if st.session_state['current_issue']:
204
- st.markdown(st.session_state['current_issue'])
 
205
  st.divider()
206
- if st.session_state['issue_analysis']:
207
- st.subheader("🤖 AI 이슈 분석")
208
- st.markdown(st.session_state['issue_analysis'])
209
  else:
210
- st.warning("AI 분석 정보를 불러오는 중이거나 실패했습니다. '새로운 이슈 발생시키기'를 다시 시도해주세요.")
211
  else:
212
- st.info(" '새로운 이슈 발생시키기' 버튼을 눌러 게임을 시작하세요.")
 
213
 
 
214
  with col_main_ui:
215
- tabs = st.tabs(["🏛️ 국가 기관 현황", "🙋‍♀️ 나의 행동 선택", "📜 이벤트 로그"])
216
 
217
  with tabs[0]:
218
- st.subheader("국가 기관별 현재 활동")
219
- # government_branches가 초기화되었는지 확인 후 표시
220
- if 'government_branches' in st.session_state:
221
- display_branch_status()
222
- else:
223
- st.warning("국가 기관 정보를 불러오는 중입니다...")
224
-
 
 
 
 
 
 
 
 
 
 
 
 
 
225
 
226
- with tabs[1]:
227
- st.subheader("영향력 행사하기")
228
- # national_status가 초기화되었는지 확인
229
- if 'national_status' in st.session_state:
230
- st.markdown(f"현재 보유 영향력: **{st.session_state['national_status']['influence_points']} P**")
231
- st.warning("행동을 선택하면 영향력 포인트가 소모됩니다.")
232
- action_buttons = {
233
- "정책 연구/제안": "행정부나 국회에 특정 정책 방향을 제안합니다.",
234
- "정보 공개 요청": "기관 활동의 투명성을 높이도록 요구합니다.",
235
- "여론 조사/캠페인": "특정 이슈에 대한 국민적 공감대를 형성하려 시도합니다.",
236
- "기관 간 협의 촉구": "갈등 상황에서 기관 간 대화를 유도합니다."
237
- }
238
- cols = st.columns(2)
239
- i = 0
240
- for action, desc in action_buttons.items():
241
- with cols[i % 2]:
242
- # 현재 이슈가 있을 때만 행동 버튼 활성화 (선택적)
243
- if st.button(action, help=desc, use_container_width=True, key=f"action_{action}", disabled=not st.session_state.get('current_issue')):
244
- take_player_action(action)
245
- i += 1
246
- if not st.session_state.get('current_issue'):
247
- st.info("새로운 이슈가 발생한 후 행동을 선택할 수 있습니다.")
248
  else:
249
- st.warning("플레이어 정보를 불러오는 중입니다...")
250
 
 
 
 
251
 
252
  with tabs[2]:
253
  st.subheader("주요 사건 기록")
254
- # event_log 초기화되었는지 확인
255
- if 'event_log' in st.session_state:
256
- log_text = "\n".join(st.session_state['event_log'][::-1]) # 최신 로그가 위로
257
- st.text_area("로그", log_text, height=300, disabled=True, key="event_log_area")
258
- else:
259
- st.warning("이벤트 로그를 불러오는 중입니다...")
260
 
261
 
262
  # --- 사이드바 ---
263
  with st.sidebar:
264
- st.header("📊 국가 현황판")
265
- # game_year가 초기화되었는지 확인
266
- if 'game_year' in st.session_state:
267
- st.markdown(f"### 현재 연도: {st.session_state['game_year']}")
268
- else:
269
- st.markdown("### 현재 연도: 불러오는 중...")
270
-
271
- # national_status가 초기화되었는지 확인
272
- if 'national_status' in st.session_state:
273
- display_national_dashboard()
274
- else:
275
- st.warning("국가 현황 정보를 불러오는 중입니다...")
276
-
277
  st.divider()
278
 
279
- if st.button("➡️ 다음 해로 넘어가기", use_container_width=True, key="next_year_button"):
280
- if st.session_state.get('current_issue'): # 현재 이슈가 있는지 확인
281
- with st.spinner(f"{st.session_state['game_year']}년 활동 결과 처리 다음 해 준비 중..."):
282
- simulate_branch_interactions() # 기관 상호작용 결과 반영
283
- st.session_state['game_year'] += 1
284
- st.session_state['national_status']['influence_points'] = st.session_state['national_status'].get('influence_points', 0) + 1 # 매년 영향력 포인트 회복 (기본값 0으로 안전하게)
285
- st.session_state['current_issue'] = None # 다음 위해 이슈 초기화
286
- st.session_state['issue_analysis'] = None
287
- # 이벤트 로그에 연도 변경 기록 (선택적)
288
- if 'event_log' in st.session_state:
289
- st.session_state['event_log'].append(f"--- {st.session_state['game_year']}년 시작 ---")
290
- st.rerun() # 화면 새로고침하여 다음 시작
291
- else:
292
- st.warning("먼저 '새로운 이슈 발생시키기'를 눌러 현재 연도의 이슈를 처리하세요.")
 
 
 
 
 
 
 
 
 
293
 
294
  st.divider()
295
- display_civics_glossary() # 시민 용어 사전 표시
296
 
297
  with st.expander("🎮 게임 가이드"):
298
- # ... (가이드 내용은 이전과 동일)
299
- st.markdown("""
300
- **대한민국 균형 잡기: 삼권분립 시뮬레이션** 게임에 오신 것을 환영합니다!
301
 
302
  **🎯 게임 목표:**
303
- 다양한 국가적 이슈에 대해 행정부, 입법부, 사법부가 어떻게 상호작용하는지 관찰하고, 때로는 시민의 입장에서 영향력을 행사하여 국가 안정도와 국민 만족도를 높이는 것입니다.
304
 
305
  **🕹️ 게임 방법:**
306
- 1. **이슈 확인:** '새로운 이슈 발생시키기' 버튼을 눌러 해당 연도의 주요 이슈와 AI 분석을 확인합니다.
307
- 2. **기관 현황 파악:** '국가 기관 현황' 탭에서 기관의 역할과 현재 활동을 살펴봅니다.
308
- 3. **행동 선택:** '나의 행동 선택' 탭에서 보유한 '영향력 포인트'를 사용하여 원하는 행동을 선택합니다. (���택 사항, 이슈 발생 가능)
309
- 4. **다음 진행:** '다음 해로 넘어가기' 버튼을 누르면 기관들의 상호작용 결과가 반영되고, 다음 연도의 새로운 이슈가 발생합니다. (현재 연도 이슈 처리 후 가능)
310
- 5. **지표 관리:** 사이드바의 '국가 현황판'을 통해 안정도, 만족도, 영향력 포인트를 꾸준히 확인하며 게임을 진행하세요.
 
311
 
312
  **💡 학습 포인트:**
313
- * 기관이 어떤 **권한**을 가지고 **역할**을 수행하는지 주목하세요.
314
- * 기관들이 서로 **견제**하거나 **협력**하는 모습을 관찰하세요. (예: 법률안 처리 과정, 정책 위법성 심사 등)
315
- * '시민 용어 사전'을 통해 어려운 용어의 뜻을 확인하세요.
 
316
 
317
- **대한민국의 균형을 잡아가는 여정을 시작해보세요!**
318
  """)
319
 
320
  if __name__ == "__main__":
321
- # 시작 government_branches가 없으면 초기화
322
- # 이 부분은 세션 상태 초기화 블록에서 이미 처리되므로 중복될 수 있으나,
323
- # 안전을 위해 한 번 더 체크하거나, 위쪽 초기화 블록을 신뢰하고 이 부분을 제거해도 됩니다.
324
- # if 'government_branches' not in st.session_state:
325
- # st.session_state['government_branches'] = initialize_branches()
326
  main()
 
1
  import streamlit as st
2
  import random
 
3
  import time
4
 
5
+ # --- 게임 설정 ---
6
+ MAX_YEARS = 5 # 대통령 임기 (5년)
7
+ INITIAL_POLITICAL_CAPITAL = 10
8
+ YEARLY_CAPITAL_GAIN = 5
9
+
10
+ # --- 정부 및 다른 기관 초기화 함수 ---
11
+ def initialize_government():
12
+ """대통령 임기 시작 시 상태 및 다른 기관 정보 초기화"""
13
+ # 초기 공약 설정 (예시)
14
+ policy_options = ["경제 성장률 5% 달성", "청년 실업률 5% 이하 달성", "부동산 시장 안정화", "외교 관계 개선", "국방력 강화", "복지 예산 10% 증액"]
15
+ selected_goals = random.sample(policy_options, 3) # 3개 공약 선택
16
+ initial_goals = {goal: 0 for goal in selected_goals} # 달성률 0%로 시작
17
+
18
+ # 국회 의석 랜덤 설정 (여소야대 가능성 포함)
19
+ total_seats = 300
20
+ gov_party_seats = random.randint(120, 170) # 여당 의석 (과반 미달 가능)
21
+ opp_party_seats = total_seats - gov_party_seats - random.randint(5, 15) # 야당 및 기타
22
 
 
 
 
 
23
  return {
24
+ 'presidency_status': {
25
+ 'approval_rating': random.randint(40, 60), # 초기 지지율
26
+ 'political_capital': INITIAL_POLITICAL_CAPITAL, # 행동력
27
+ 'policy_goals': initial_goals, # 공약 및 달성률(%)
28
+ 'stability': 100, # 국가 안정도
29
+ 'congress_relations': random.randint(40, 60) # 국회 관계 (0~100)
30
+ },
31
+ 'other_branches': {
32
+ 'Congress': { # 국회
33
+ 'name': '국회 (입법부)',
34
+ 'seats': {'Government': gov_party_seats, 'Opposition': opp_party_seats, 'Others': total_seats - gov_party_seats - opp_party_seats},
35
+ 'stance': '사안별 협조 및 견제', # 초기 입장
36
+ 'key_focus': random.sample(['민생 안정', '정부 견제', '개혁 입법', '지역 예산 확보'], 2), # 주요 관심사
37
+ 'pending_bills': [], # 대통령 제출 법안 등
38
+ 'relations_score': 50 # 대통령 관계 점수 (presidency_status와 연동)
39
+ },
40
+ 'Judiciary': { # 사법부
41
+ 'name': '법원/헌법재판소 (사법부)',
42
+ 'status': '주요 현안 관련 소송 없음',
43
+ 'independence_level': random.randint(75, 95), # 사법부 독립성 지수
44
+ 'pending_cases': [] # 심리 중인 주요 사건
45
+ }
46
+ },
47
+ 'event_log': [f"제 {random.randint(19, 22)}대 대통령 임기 시작"] # 임기 시작 로그
48
  }
49
 
50
  # --- 세션 상태 초기화 ---
51
+ if 'presidency_year' not in st.session_state:
52
+ st.session_state['presidency_year'] = 1
53
+ if 'game_state' not in st.session_state:
54
+ # 게임 시작 한 번만 초기화
55
+ st.session_state['game_state'] = initialize_government()
56
+ st.session_state.game_state['event_log'].append(f"초기 국정 지지도: {st.session_state.game_state['presidency_status']['approval_rating']}%")
57
+ st.session_state.game_state['event_log'].append(f"선택된 주요 공약: {', '.join(st.session_state.game_state['presidency_status']['policy_goals'].keys())}")
58
+ if 'current_agenda' not in st.session_state:
59
+ st.session_state['current_agenda'] = None
60
+ if 'agenda_briefing' not in st.session_state:
61
+ st.session_state['agenda_briefing'] = None
62
+ if 'game_over' not in st.session_state:
63
+ st.session_state['game_over'] = False
64
+ if 'action_taken_this_year' not in st.session_state:
65
+ st.session_state['action_taken_this_year'] = False # 연도별 행동 1회 제한 등 가능
66
+
67
+ # --- 국정 현안 생성 함수 (Placeholder) ---
68
+ def generate_agenda(year, game_state):
69
+ """현실적인 국정 현안 생성 (현재는 Placeholder)"""
70
+ agendas = [
71
+ {"title": "글로벌 공급망 위기 심화", "description": "주요 교역국의 경제 불안과 물류 차질로 인해 국내 수출입에 차질이 발생하고 있으며, 핵심 산업의 부품 수급난이 현실화되고 있습니다. 산업계 보호와 경제 충격 완화를 위한 정부의 선제적 대응이 요구됩니다.", "type": "economy"},
72
+ {"title": "대규모 감염병 재확산 조짐", "description": "해외 유입 변이 바이러스 확산으로 감염병 확진자 수가 다시 증가세를 보이고 있습니다. 의료 시스템 부담 가중과 사회적 거리두기 강화 여부를 두고 정부의 신속하고 정확한 판단이 중요해졌습니다.", "type": "social"},
73
+ {"title": "부동산 가격 급등 및 불안 심리 확산", "description": "수도권을 중심으로 주택 가격이 다시 급등하며 무주택자의 불안감이 커지고 있습니다. 강력한 추가 규제 또는 공급 확대 정책을 두고 여론이 갈리고 있으며, 국회에서도 관련 논의가 활발합니다.", "type": "social"},
74
+ {"title": "주변국과의 외교적 마찰 발생", "description": "역사 문제 또는 해양 경계선을 둘러싼 갈등으로 주변국과의 외교적 긴장감이 높아졌습니다. 양국 관계 악화가 경제 및 안보에 미칠 파장을 최소화하기 위한 섬세한 외교적 노력이 필요합니다.", "type": "diplomacy"},
75
+ {"title": "국회, 정부조직 개편 법안 발의", "description": "야당을 중심으로 정부 부처 통폐합 및 기능 조정을 골자로 하는 정부조직법 개정안이 발의되었습니다. 효율성 증대라는 명분과 정치적 공세라는 해석이 엇갈리며, 대통령실의 입장 표명이 요구되고 있습니다.", "type": "politics"},
76
+ {"title": "대형 산업 재해 발생 및 안전 규제 강화 요구", "description": "대규모 공사 현장에서 중대 산업 재해가 발생하여 다수의 인명 피해가 발생했습니다. 기업 처벌 강화와 작업장 안전 규제 전면 개정을 요구하는 목소리가 높아지고 있으며, 관련 입법 논의가 불가피해 보입니다.", "type": "social"}
77
+ ]
78
+ chosen_agenda = random.choice(agendas)
79
+ log_message = f"{year}년차 주요 현안: {chosen_agenda['title']}"
80
+ if 'event_log' in game_state:
81
+ game_state['event_log'].append(log_message)
82
+ return chosen_agenda
83
+
84
+ # --- 현안 브리핑 제공 함수 (Placeholder) ---
85
+ def provide_agenda_briefing(agenda, game_state):
86
+ """AI 대신 Placeholder 브리핑 제공"""
87
+ title = agenda['title']
88
+ briefing = f"**현안 브리핑: {title}**\n\n"
89
+ briefing += f"대통령님, 현재 '{title}' 현안에 대한 대응이 시급합니다.\n"
90
+
91
+ # 예시: 대통령이 취할 수 있는 조치 제안 (현안 타입별 분기 가능)
92
+ options = []
93
+ if agenda['type'] in ['economy', 'social']:
94
+ options.append({
95
+ "action": "긴급 경제/민생 안정 대책 발표 (행정명령/지시)",
96
+ "cost": 3,
97
+ "pros": "신속한 대응 가능, 단기적 여론 안정 효과.",
98
+ "cons": "근본 해결 어려움, 법적 근거 논란 시 사법부 제동 가능성, 국회 반발 시 정치적 부담.",
99
+ "congress_impact": -5, # 국회 관계 영향 (예시)
100
+ "judiciary_risk": 0.2 # 사법부 개입 확률 (예시)
101
+ })
102
+ options.append({
103
+ "action": "관련 법률안/추경 예산안 국회 제출",
104
+ "cost": 4,
105
+ "pros": "근본적 해결 기반 마련, 통과 정책 성공률 기여.",
106
+ "cons": f"국회 통과 불확실 (현재 관계: {game_state['other_branches']['Congress']['relations_score']}/100), 장시간 소요, 여야 협상 난항 예상.",
107
+ "congress_impact": 10, # 통과 시 관계 개선 기대 (예시)
108
+ "judiciary_risk": 0.05
109
+ })
110
+ if agenda['type'] == 'diplomacy':
111
+ options.append({
112
+ "action": "외교 특사 파견 및 고위급 회담 추진",
113
+ "cost": 3,
114
+ "pros": "직접적 대화를 통한 갈등 완화 시도.",
115
+ "cons": "상대국 거부 외교적 고립 심화 가능성, 국내 강경 여론 부담.",
116
+ "congress_impact": 0,
117
+ "judiciary_risk": 0
118
+ })
119
+ if agenda['type'] == 'politics':
120
+ options.append({
121
+ "action": "국회와 협상 시도 (여야 지도부 면담)",
122
+ "cost": 2,
123
+ "pros": "정치적 타협점 모색, 국회 관계 개선 기회.",
124
+ "cons": "협상 결렬 리더십 타격, 시간 소모.",
125
+ "congress_impact": 5,
126
+ "judiciary_risk": 0
127
+ })
128
+ options.append({
129
+ "action": "대국민 담화 발표 (여론전)",
130
+ "cost": 1,
131
+ "pros": "국정 운영 방향 설명, 지지층 결집 시도.",
132
+ "cons": "설득력 부족 역풍 가능성, 지지도 변동성 큼.",
133
+ "congress_impact": -2,
134
+ "judiciary_risk": 0
135
+ })
136
+
137
+ briefing += "\n**대응 방안 제안:**\n"
138
+ for i, opt in enumerate(options):
139
+ briefing += f"{i+1}. **{opt['action']}** (정치력 소모: {opt['cost']})\n"
140
+ briefing += f" - **긍정:** {opt['pros']}\n"
141
+ briefing += f" - **부정:** {opt['cons']}\n"
142
+ # 간단한 예상 결과 추가
143
+ briefing += f" - *예상 국회 반응:* 관계 점수 {opt['congress_impact']:+}점 변동 가능성\n"
144
+ briefing += f" - *예상 사법부 리스크:* {opt['judiciary_risk']*100:.0f}% 확률로 관련 소송/심판 가능성\n\n"
145
+
146
+ log_message = f" - 현안 '{title}'에 대한 브리핑 완료."
147
+ if 'event_log' in game_state:
148
+ game_state['event_log'].append(log_message)
149
+
150
+ # 브리핑 텍스트와 함께 선택 옵션 데이터 반환
151
+ return {"text": briefing, "options": options}
152
+
153
+ # --- 대통령 행동 실행 함수 ---
154
+ def execute_presidential_action(action_index, briefing_data, game_state):
155
+ """선택된 대통령 행동을 실행하고 게임 상태 업데이트"""
156
+ options = briefing_data.get("options", [])
157
+ if not (0 <= action_index < len(options)):
158
+ st.error("잘못된 행동 선택입니다.")
159
  return False
160
 
161
+ selected_action = options[action_index]
162
+ action_name = selected_action['action']
163
+ cost = selected_action['cost']
164
+ presidency_status = game_state['presidency_status']
165
+ other_branches = game_state['other_branches']
166
+
167
+ if presidency_status['political_capital'] < cost:
168
+ st.warning(f"정치력이 부족합니다! (필요: {cost}, 보유: {presidency_status['political_capital']})")
169
+ return False
170
+
171
+ # 정치력 소모
172
+ presidency_status['political_capital'] -= cost
173
+ log_message = f" - 대통령 지시: '{action_name}' 수행 (정치력 {cost} 소모)"
174
+ game_state['event_log'].append(log_message)
175
+ st.success(f"'{action_name}' 지시 완료.")
176
+
177
+ # 행동 유형별 게임 상태 업데이트 (간단한 예시)
178
+ if "법률안" in action_name or "예산안" in action_name:
179
+ bill_name = f"{st.session_state['presidency_year']}년차-{agenda['title'][:10]}-관련법안"
180
+ other_branches['Congress']['pending_bills'].append({"name": bill_name, "status": "제출됨", "origin": "President"})
181
+ game_state['event_log'].append(f" - '{bill_name}'이(가) 국회에 제출되었습니다.")
182
+ # 국회 관계에 즉각적인 영향은 주지 않고, 통과 여부에서 반영
183
+ elif "행정명령" in action_name or "대책 발표" in action_name:
184
+ # 즉각적인 안정도/지지도 영향 (소폭)
185
+ presidency_status['stability'] = min(100, presidency_status['stability'] + random.randint(1, 5))
186
+ presidency_status['approval_rating'] += random.randint(-2, 3)
187
+ # 사법부 리스크 반영 (다음 턴 시뮬레이션에서 처리)
188
+ if random.random() < selected_action.get('judiciary_risk', 0):
189
+ case_name = f"{st.session_state['presidency_year']}년차-{action_name[:10]}-위헌소송"
190
+ other_branches['Judiciary']['pending_cases'].append({"name": case_name, "type": "Constitutional Review"})
191
+ game_state['event_log'].append(f" - [경고] '{action_name}' 관련하여 위헌 소송이 제기될 가능성이 포착되었습니다!")
192
+ elif "협상" in action_name:
193
+ # 국회 관계 점수 소폭 상승 시도
194
+ change = random.randint(3, 8)
195
+ other_branches['Congress']['relations_score'] = min(100, other_branches['Congress']['relations_score'] + change)
196
+ game_state['event_log'].append(f" - 국회와의 관계가 소폭 개선되었습니다. (관계 점수 {change:+}점)")
197
+ elif "대국민 담화" in action_name:
198
+ # 지지도 변동폭 크게
199
+ change = random.randint(-5, 7)
200
+ presidency_status['approval_rating'] += change
201
+ game_state['event_log'].append(f" - 대국민 담화 결과, 국정 지지도가 {change:+}%p 변동했습니다.")
202
+ elif "거부권 행사" in action_name: # 거부권은 시뮬레이션 단계에서 국회가 통과시킨 법안에 대해 행사
203
+ pass # 실제 로직은 simulate 단계에서 처리
204
+
205
+ # 지지도/관계 점수 0~100 범위 유지
206
+ presidency_status['approval_rating'] = max(0, min(100, presidency_status['approval_rating']))
207
+ other_branches['Congress']['relations_score'] = max(0, min(100, other_branches['Congress']['relations_score']))
208
+
209
+ st.session_state['action_taken_this_year'] = True # 행동 완료 표시
210
+ return True
211
+
212
+ # --- 견제와 균형 시뮬레이션 함수 ---
213
+ def simulate_checks_and_balances(game_state):
214
+ """국회와 사법부의 반응을 시뮬레이션하고 결과 반영"""
215
+ presidency_status = game_state['presidency_status']
216
+ congress = game_state['other_branches']['Congress']
217
+ judiciary = game_state['other_branches']['Judiciary']
218
+ log = game_state['event_log']
219
+ year = st.session_state['presidency_year']
220
+
221
+ log.append(f"--- {year}년차 연말 정국 결산 ---")
222
+
223
+ # 1. 국회 시뮬레이션 (법안 처리 등)
224
+ processed_bills = []
225
+ for i, bill in enumerate(congress['pending_bills']):
226
+ if bill['status'] == '제출됨':
227
+ # 법안 통과 확률 계산 (더 정교화 가능)
228
+ pass_chance = 0.3 # 기본 통과 확률
229
+ pass_chance += (congress['relations_score'] - 50) * 0.005 # 국회 관계 영향
230
+ pass_chance += (presidency_status['approval_rating'] - 50) * 0.003 # 대통령 지지율 영향
231
+ # 여소야대 페널티 (예시)
232
+ if congress['seats']['Government'] < (sum(congress['seats'].values()) / 2):
233
+ pass_chance -= 0.15
234
+
235
+ pass_chance = max(0.05, min(0.95, pass_chance)) # 5% ~ 95%
236
+
237
+ if random.random() < pass_chance:
238
+ bill['status'] = '통과'
239
+ log.append(f" - [국회] '{bill['name']}' 법안이 국회를 통과했습니다!")
240
+ # 정책 목표 달성률 증가 (연관성 판단 필요 - 여기서는 랜덤하게 1개 목표 증가)
241
+ if presidency_status['policy_goals']:
242
+ goal_to_update = random.choice(list(presidency_status['policy_goals'].keys()))
243
+ progress = random.randint(5, 15)
244
+ presidency_status['policy_goals'][goal_to_update] = min(100, presidency_status['policy_goals'][goal_to_update] + progress)
245
+ log.append(f" - 공약 '{goal_to_update}' 달성률이 {progress}%p 상승했습니다.")
246
+ presidency_status['approval_rating'] += random.randint(1, 4) # 지지도 소폭 상승
247
+ congress['relations_score'] = min(100, congress['relations_score'] + random.randint(2, 6)) # 관계 개선
248
+ else:
249
+ bill['status'] = '부결'
250
+ log.append(f" - [국회] '{bill['name']}' 법안이 국회에서 부결되었습니다.")
251
+ presidency_status['approval_rating'] -= random.randint(1, 3) # 지지도 소폭 하락
252
+ congress['relations_score'] = max(0, congress['relations_score'] - random.randint(1, 4)) # 관계 악화
253
+
254
+ processed_bills.append(i) # 처리된 법안 인덱스 저장
255
+
256
+ # 처리된 법안 목록에서 제거 (뒤에서부터 제거해야 인덱스 문제 없음)
257
+ for i in sorted(processed_bills, reverse=True):
258
+ del congress['pending_bills'][i]
259
+
260
+ # 국회 랜덤 이벤트 (예: 국정조사 발동)
261
+ if random.random() < 0.1: # 10% 확률
262
+ log.append(" - [국회] 특정 사안에 대한 국정조사가 발동되었습니다. 정부 부처 긴장 고조.")
263
+ presidency_status['stability'] -= random.randint(3, 7)
264
+ congress['relations_score'] = max(0, congress['relations_score'] - random.randint(5, 10))
265
+
266
+ # 2. 사법부 시뮬레이션 (위헌 심사 등)
267
+ ruled_cases = []
268
+ for i, case in enumerate(judiciary['pending_cases']):
269
+ # 판결 확률 계산 (독립성 지수 영향)
270
+ uphold_chance = 0.6 # 기본 합헌/적법 확률
271
+ uphold_chance += (50 - judiciary['independence_level']) * 0.005 # 독립성 낮으면 정부 유리? (논쟁적)
272
+
273
+ if random.random() < uphold_chance:
274
+ case['status'] = '합헌/적법'
275
+ log.append(f" - [사법부] '{case['name']}'에 대해 합헌/적법 결정이 내려졌습니다.")
276
+ else:
277
+ case['status'] = '위헌/위법'
278
+ log.append(f" - [사법부] '{case['name']}'에 대해 위헌/위법 결정이 내려졌습니다! 관련 정책 효력 상실.")
279
+ presidency_status['approval_rating'] -= random.randint(3, 8) # 지지도 하락
280
+ presidency_status['stability'] -= random.randint(2, 6) # 안정도 하락
281
+ # 관련 정책 목표 후퇴 (구현 필요)
282
+
283
+ ruled_cases.append(i)
284
+
285
+ for i in sorted(ruled_cases, reverse=True):
286
+ del judiciary['pending_cases'][i]
287
+
288
+ # 3. 연말 최종 상태 업데이트
289
+ presidency_status['approval_rating'] += random.randint(-2, 2) # 자연 변동
290
+ presidency_status['stability'] += random.randint(-3, 1) # 자연 변동
291
+ # 지지도/안정도/관계 점수 범위 유지
292
+ presidency_status['approval_rating'] = max(0, min(100, presidency_status['approval_rating']))
293
+ presidency_status['stability'] = max(0, min(100, presidency_status['stability']))
294
+ congress['relations_score'] = max(0, min(100, congress['relations_score']))
295
+
296
+ log.append(f"--- {year}년차 국정 결산 완료 ---")
297
 
298
 
299
  # --- UI 표시 함수들 ---
300
+ def display_presidency_dashboard(game_state):
301
+ status = game_state['presidency_status']
302
+ st.metric("국정 지지도", f"{status['approval_rating']}%")
303
+ st.metric("대통령 정치력", f"{status['political_capital']} P")
304
+ st.metric("국가 안정도", f"{status['stability']} / 100")
305
+ st.metric("국회 관계", f"{game_state['other_branches']['Congress']['relations_score']} / 100")
306
+
307
+ st.subheader("주요 공약 달성률")
308
+ for goal, progress in status['policy_goals'].items():
309
+ st.progress(progress / 100, text=f"{goal} ({progress}%)")
310
+
311
+ def display_other_branches_status(game_state):
312
+ congress = game_state['other_branches']['Congress']
313
+ judiciary = game_state['other_branches']['Judiciary']
314
+
315
+ with st.expander(f"{congress['name']} (관계 점수: {congress['relations_score']})"):
316
+ st.markdown(f"**의석 분포:** 여당 {congress['seats']['Government']}석 / 야당 {congress['seats']['Opposition']}석 / 기타 {congress['seats']['Others']}석")
317
+ st.markdown(f"**현재 입장:** {congress['stance']}")
318
+ st.markdown(f"**주요 관심사:** {', '.join(congress['key_focus'])}")
319
+ if congress['pending_bills']:
320
+ st.markdown("**계류 중인 주요 법안:**")
321
+ for bill in congress['pending_bills']:
322
+ st.markdown(f"- {bill['name']} ({bill.get('status', '제출됨')})")
323
+ else:
324
+ st.markdown("**계류 중인 주요 법안 없음**")
325
+
326
+ with st.expander(f"{judiciary['name']} (독립성: {judiciary['independence_level']})"):
327
+ st.markdown(f"**현재 상태:** {judiciary['status']}")
328
+ if judiciary['pending_cases']:
329
+ st.markdown("**심리 중인 주요 사건:**")
330
+ for case in judiciary['pending_cases']:
331
+ st.markdown(f"- {case['name']} ({case.get('type', '일반')})")
332
+ else:
333
+ st.markdown("**심리 중인 주요 사건 없음**")
334
+
335
+ def display_gov_terms_glossary():
336
  glossary = {
337
+ "법률안 거부권 (재의 요구권)": "국회가 통과시킨 법률안에 대해 대통령이 이의가 있을 때, 공포하지 않고 국회에 다시 심의해달라고 요구할 수 있는 권한. 국회는 재적의원 과반수 출석과 출석의원 3분의 2 이상의 찬성으로 재의결 가능.",
338
+ "행정명령": "대통령이 법률에서 위임받은 사항이나 법률을 집행하기 위해 필요한 사항에 대해 발하는 명령 (대통령령). 법률의 범위 내에서만 가능하며, 위헌/위법 시 사법부의 통제를 받음.",
339
+ "예산안 편성권": "정부(대통령)가 다음 해의 국가 살림 계획인 예산안을 짜서 국회에 제출할 수 있는 권한. 국회는 심의 과정에서 수정 가능.",
340
+ "국회 예산 심의/확정권": "정부가 제출한 예산안을 국회가 심사하고 최종적으로 확정하는 권한. 정부 예산안을 삭감하거나 증액(정부 동의 필요)할 수 있음.",
341
+ "위헌법률심판권": "국회가 만든 법률이 헌법에 위배되는지 여부를 헌법재판소가 심판하는 권한.",
342
+ "명령·규칙·처분 심사권": "대통령령 등 행정기관이 만든 명령이나 규칙, 또는 행정 처분이 헌법이나 법률에 위반되는지 여부를 대법원이 최종적으로 심사하는 권한.",
343
+ "국정감사/조사권": "국회가 정부의 국정 운영 전반이나 특정 사안에 대해 잘못된 점이 없는지 감사하거나 조사할 수 있는 권한. 행정부를 견제하는 주요 수단.",
344
+ "탄핵소추권": "대통령 고위 공직자가 직무상 중대한 헌법이나 법률 위반 시, 국회가 파면을 결정해달라고 헌법재판소에 청구할 수 있는 권한.",
345
+ "인사청문회": "대통령이 임명하는 고위 공직 후보자에 대해 국회가 직무수행 능력과 도덕성 등을 검증하는 절차. 일부 직위는 국회 동의가 필수적.",
346
+ "정치력 (Political Capital)": "대통령이 자신의 정책을 추진하거나 영향력을 행사하는 데 사용할 수 있는 비공식적인 자원. 지지도, 협상력, 리더십 등을 포함하는 개념으로 게임 내 행동력으로 사용됨.",
347
  }
348
+ with st.sidebar.expander("🏛️ 정부 용어 사전", expanded=False):
349
  for term, definition in glossary.items():
350
  st.markdown(f"**{term}:** {definition}")
351
  st.markdown("---")
352
 
353
  # --- 메인 게임 루프 ---
354
  def main():
355
+ st.title("👑 대통령의 균형추: 삼권분립 리더십 시뮬레이션")
356
+
357
+ if st.session_state['game_over']:
358
+ st.balloons()
359
+ st.header("임기 종료")
360
+ st.subheader("최종 국정 운영 결과")
361
+ final_status = st.session_state.game_state['presidency_status']
362
+ st.metric("최종 국정 지지도", f"{final_status['approval_rating']}%")
363
+ st.metric("최종 국가 안정도", f"{final_status['stability']}")
364
+ st.metric("최종 국회 관계", f"{st.session_state.game_state['other_branches']['Congress']['relations_score']}")
365
+ st.write("주요 공약 최종 달성률:")
366
+ for goal, progress in final_status['policy_goals'].items():
367
+ st.progress(progress / 100, text=f"{goal} ({progress}%)")
368
+ st.subheader("국정 운영 기록")
369
+ log_text = "\n".join(st.session_state.game_state['event_log'][::-1])
370
+ st.text_area("로그", log_text, height=400, disabled=True, key="final_log")
371
+ if st.button("다시 시작하기"):
372
+ # 세션 상태 초기화 및 새로고침
373
+ for key in list(st.session_state.keys()):
374
+ del st.session_state[key]
375
+ st.rerun()
376
+ return # 게임 오버 시 메인 루프 종료
377
+
378
+ col_agenda, col_main_ui = st.columns([2, 3])
379
+
380
+ # --- 왼쪽: 국정 현안 브리핑 ---
381
+ with col_agenda:
382
+ st.header(f"📰 {st.session_state['presidency_year']}년차 국정 현안")
383
+
384
+ # 새 현안 생성 버튼 (연도 시작 시 자동으로 생성되도록 변경)
385
+ if st.session_state['current_agenda'] is None:
386
+ with st.spinner("새로운 국정 현안 보고 준비 중..."):
387
+ time.sleep(1) # 로딩 효과
388
+ agenda = generate_agenda(st.session_state['presidency_year'], st.session_state.game_state)
389
+ if agenda:
390
+ st.session_state['current_agenda'] = agenda
391
+ st.session_state['agenda_briefing'] = provide_agenda_briefing(agenda, st.session_state.game_state)
392
+ st.session_state['action_taken_this_year'] = False # 새 현안 발생 시 행동 가능
393
+ st.rerun() # 즉시 반영
394
  else:
395
+ st.error("현안 생성에 실패했습니다.")
396
 
397
+ if st.session_state['current_agenda']:
398
+ st.subheader(f"현안: {st.session_state['current_agenda']['title']}")
399
+ st.markdown(st.session_state['current_agenda']['description'])
400
  st.divider()
401
+ if st.session_state['agenda_briefing']:
402
+ st.subheader("📝 보좌진 브리핑 및 대응 방안")
403
+ st.markdown(st.session_state['agenda_briefing']['text'])
404
  else:
405
+ st.warning("브리핑 정보를 불러오는 중입니다.")
406
  else:
407
+ # 부분은 로직에 의해 거의 표시되지 않음
408
+ st.info("다음 연도 국정 현안을 기다리고 있습니다.")
409
 
410
+ # --- 오른쪽: 메인 UI (탭) ---
411
  with col_main_ui:
412
+ tabs = st.tabs(["🏛️ 대통령 집무실 (행동 선택)", "📊 국회 사법부 동향", "📜 국정 운영 로그"])
413
 
414
  with tabs[0]:
415
+ st.subheader("대통령 지시 사항")
416
+ if st.session_state['current_agenda'] and st.session_state['agenda_briefing']:
417
+ if not st.session_state['action_taken_this_year']:
418
+ st.markdown(f"현재 정치력: **{st.session_state.game_state['presidency_status']['political_capital']} P**")
419
+ options = st.session_state['agenda_briefing'].get("options", [])
420
+ num_options = len(options)
421
+ cols = st.columns(2) # 2열 배치
422
+
423
+ for i, opt in enumerate(options):
424
+ col_index = i % 2
425
+ with cols[col_index]:
426
+ button_label = f"{opt['action']} ({opt['cost']}P)"
427
+ button_key = f"action_{st.session_state['presidency_year']}_{i}" # 연도별 고유 키
428
+ if st.button(button_label, key=button_key, use_container_width=True,
429
+ help=f"긍정: {opt['pros']} / 부정: {opt['cons']}",
430
+ disabled=(st.session_state.game_state['presidency_status']['political_capital'] < opt['cost'])):
431
+ execute_presidential_action(i, st.session_state['agenda_briefing'], st.session_state.game_state)
432
+ st.rerun() # 행동 후 즉시 상태 업데이트 반영
433
+ else:
434
+ st.info(f"{st.session_state['presidency_year']}년차 주요 지시는 이미 내려졌습니다. '다음 해 국정 운영' 버튼을 눌러주세요.")
435
 
436
+ elif st.session_state['action_taken_this_year']:
437
+ st.info(f"{st.session_state['presidency_year']}년차 주요 지시는 이미 내려졌습니다. '다음 해 국정 운영' 버튼을 눌러주세요.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
438
  else:
439
+ st.info("국정 현안 브리핑을 기다리고 있습니다.")
440
 
441
+ with tabs[1]:
442
+ st.subheader("다른 국가기관 동향")
443
+ display_other_branches_status(st.session_state.game_state)
444
 
445
  with tabs[2]:
446
  st.subheader("주요 사건 기록")
447
+ log_text = "\n".join(st.session_state.game_state['event_log'][::-1]) # 최신 로그가 위로
448
+ st.text_area("로그", log_text, height=400, disabled=True, key="event_log_area")
 
 
 
 
449
 
450
 
451
  # --- 사이드바 ---
452
  with st.sidebar:
453
+ st.header("📊 국정 현황판")
454
+ st.markdown(f"### 임기 {st.session_state['presidency_year']} / {MAX_YEARS} 년차")
455
+ display_presidency_dashboard(st.session_state.game_state)
 
 
 
 
 
 
 
 
 
 
456
  st.divider()
457
 
458
+ # 다음 해로 넘어가기 버튼
459
+ if st.button("➡️ 다음 국정 운영", use_container_width=True, key="next_year_button",
460
+ disabled=not st.session_state['action_taken_this_year']): # 행동을 해야 다음 해로
461
+ with st.spinner(f"{st.session_state['presidency_year']}년차 국정 결산다음 해 준비 중..."):
462
+ simulate_checks_and_balances(st.session_state.game_state) # 시뮬레이션 실행
463
+ st.session_state['presidency_year'] += 1
464
+ # 연간 정치력 회복
465
+ st.session_state.game_state['presidency_status']['political_capital'] += YEARLY_CAPITAL_GAIN
466
+ # 다음 위해 현안/브리핑 초기화
467
+ st.session_state['current_agenda'] = None
468
+ st.session_state['agenda_briefing'] = None
469
+ st.session_state['action_taken_this_year'] = False # 새해에는 행동 가능
470
+
471
+ # 게임 오버 조건 확인
472
+ if st.session_state['presidency_year'] > MAX_YEARS:
473
+ st.session_state['game_over'] = True
474
+ st.session_state.game_state['event_log'].append(f"--- 임기 종료 ---")
475
+
476
+ time.sleep(1.5) # 결과 처리 시간 보여주기
477
+ st.rerun() # 화면 새로고침하여 다음 턴 시작
478
+
479
+ if not st.session_state['action_taken_this_year'] and st.session_state['current_agenda']:
480
+ st.warning("올해 국정 현안에 대한 지시를 내려주세요.")
481
 
482
  st.divider()
483
+ display_gov_terms_glossary() # 정부 용어 사전 표시
484
 
485
  with st.expander("🎮 게임 가이드"):
486
+ st.markdown(f"""
487
+ **대통령의 균형추: 삼권분립 리더십 시뮬레이션** 게임에 오신 것을 환영합니다, 대통령님!
 
488
 
489
  **🎯 게임 목표:**
490
+ {MAX_YEARS}년의 임기 동안 발생하는 국정 현안에 대응하고, 주요 공약을 이행하며 성공적인 대통령으로 임기를 마치는 것입니다. 과정에서 국회(입법부)의 협력과 견제, 사법부의 심사를 경험하며 삼권분립의 원리를 체감하게 됩니다.
491
 
492
  **🕹️ 게임 방법:**
493
+ 1. **현안 브리핑 확인 (매년 초):** 새로운 국정 현안과 보좌진의 대응 방안 브리핑을 확인합니다. (왼쪽 영역)
494
+ 2. **대통령 지시 내리기:** '대통령 집무실' 탭에서 제시된 대응 방안 하나를 선택하여 지시를 내립니다. 행동에는 '정치력(P)'이 소모됩니다. (오른쪽 탭 1)
495
+ 3. **다른 기관 동향 파악:** '국회 사법부 동향' 탭에서 현재 국회와의 관계, 계류 법안, 사법부의 주요 활동 등을 확인합니다. (오른쪽 2)
496
+ 4. **국정 운영 로그 확인:** 대통령의 지시, 국회/사법부의 반응, 지지도 변화 주요 사건 기록을 확인합니다. (오른쪽 3)
497
+ 5. **다음 국정 운영:** 사이드바의 '다음 해 국정 운영' 버튼을 눌러 연말 국정 결산(국회/사법부 반응 시뮬레이션)진행하고 다음 해로 넘어갑니다. (대통령 지시를 내려야 활성화됨)
498
+ 6. **지표 관리:** 사이드바의 '국정 현황판'에서 지지도, 정치력, 공약 달성률 등을 꾸준히 확인하며 임기를 운영하세요.
499
 
500
  **💡 학습 포인트:**
501
+ * 대통령의 **권한**(행정명령, 법안제출권 등)과 **한계**(정치력 소모, 국회/사법부 견제)를 느껴보세요.
502
+ * 국회와의 **관계 점수**가 법안 통과 등에 미치는 영향을 관찰하세요. (협상과 견제)
503
+ * 대통령의 결정이 때로는 **사법부의 심판** 대상이 수 있음을 확인하세요. (위헌/위법 결정)
504
+ * '정부 용어 사전'을 통해 관련 용어를 익혀보세요.
505
 
506
+ **성공적인 국정 운영을 기원합니다!**
507
  """)
508
 
509
  if __name__ == "__main__":
510
+ # 필요시 초기화 로직 추가 가능 (현재는 세션 상태 블록에서 처리)
 
 
 
 
511
  main()