File size: 90,534 Bytes
fd8f1df ba63f2d 5cdb81a ba25b53 ba63f2d fd8f1df f3edf46 ba25b53 f3edf46 3d57d8f ba25b53 3d57d8f ba25b53 3d57d8f ba25b53 3d57d8f ba25b53 fd8f1df ba25b53 fd8f1df ba25b53 fd8f1df ba25b53 fd8f1df ba25b53 3d57d8f ba25b53 fd8f1df ba25b53 fd8f1df 3d57d8f f3edf46 3d57d8f ba25b53 3d57d8f ba25b53 2e88a0b f3edf46 3d57d8f ba25b53 2e88a0b 3d57d8f baca1c4 f3edf46 3d57d8f 2e88a0b 3d57d8f f3edf46 2e88a0b fd8f1df 3d57d8f ba25b53 2e88a0b ba25b53 f3edf46 2e88a0b fd8f1df 3d57d8f ba25b53 3d57d8f ba25b53 f3edf46 ba25b53 2e88a0b 3d57d8f baca1c4 ed648bd 3d57d8f ba25b53 3d57d8f ba25b53 3d57d8f ba25b53 3d57d8f ba25b53 fd8f1df ba25b53 fd8f1df ba25b53 fd8f1df ba25b53 3d57d8f ba25b53 3d57d8f fd8f1df ba25b53 fd8f1df 3d57d8f ba25b53 3d57d8f ba25b53 fd8f1df 3d57d8f ba25b53 3d57d8f ba25b53 3d57d8f fd8f1df 3d57d8f ba25b53 2e88a0b ba25b53 2e88a0b ba25b53 3d57d8f ba25b53 2e88a0b ba25b53 2e88a0b ba25b53 2e88a0b fd8f1df ba25b53 fd8f1df ba25b53 2e88a0b fd8f1df 2e88a0b fd8f1df ba25b53 2e88a0b ba25b53 fd8f1df 2e88a0b fd8f1df ba25b53 2e88a0b ba25b53 3d57d8f ba25b53 3d57d8f ba25b53 3d57d8f ba25b53 3d57d8f ba25b53 3d57d8f ba25b53 3d57d8f ba25b53 3d57d8f ba25b53 2e88a0b ba25b53 2e88a0b ba25b53 2e88a0b ba25b53 fd8f1df ba25b53 fd8f1df ba25b53 fd8f1df ba25b53 fd8f1df ba25b53 f3edf46 ba25b53 2e88a0b ba25b53 3d57d8f fd8f1df 3d57d8f ba25b53 fd8f1df ba25b53 fd8f1df 2e88a0b 3d57d8f ba25b53 fd8f1df 3d57d8f 2e88a0b 3d57d8f fd8f1df 3d57d8f fd8f1df 3d57d8f fd8f1df 3d57d8f fd8f1df 3d57d8f ba25b53 fd8f1df f3edf46 fd8f1df ba25b53 f3edf46 2e88a0b f3edf46 ba25b53 3d57d8f ba25b53 3d57d8f fd8f1df 3d57d8f f3edf46 fd8f1df 5cdb81a f3edf46 ba25b53 2e88a0b f3edf46 fd8f1df f3edf46 fd8f1df f3edf46 fd8f1df f3edf46 fd8f1df f3edf46 ba25b53 2e88a0b 3d57d8f ba25b53 2e88a0b 3d57d8f ba25b53 2e88a0b 3d57d8f ba25b53 fd8f1df ba25b53 2e88a0b 3d57d8f 2e88a0b 3d57d8f 2e88a0b fd8f1df 2e88a0b 3d57d8f fd8f1df 2e88a0b ba25b53 fd8f1df 2e88a0b f3edf46 2e88a0b f3edf46 fd8f1df f3edf46 2e88a0b f3edf46 ba25b53 3d57d8f ba25b53 3d57d8f ba25b53 fd8f1df f3edf46 2e88a0b f3edf46 fd8f1df f3edf46 ba25b53 2e88a0b fd8f1df ba25b53 2e88a0b f3edf46 fd8f1df 3d57d8f ba25b53 3d57d8f ba25b53 3d57d8f fd8f1df f3edf46 fd8f1df 2e88a0b ba25b53 fd8f1df 3d57d8f ba25b53 3d57d8f 2e88a0b 3d57d8f 2e88a0b fd8f1df f3edf46 fd8f1df 2e88a0b 3d57d8f fd8f1df 3d57d8f fd8f1df 3d57d8f ba25b53 3d57d8f fd8f1df 3d57d8f fd8f1df 3d57d8f ba25b53 2e88a0b 3d57d8f fd8f1df 3d57d8f fd8f1df 3d57d8f fd8f1df 3d57d8f fd8f1df ba25b53 3d57d8f fd8f1df 3d57d8f fd8f1df 3d57d8f fd8f1df 2e88a0b fd8f1df 3d57d8f fd8f1df 3d57d8f fd8f1df 2e88a0b ba25b53 2e88a0b fd8f1df ba25b53 3d57d8f ba25b53 3d57d8f fd8f1df 3d57d8f f3edf46 fd8f1df 5cdb81a ba25b53 3d57d8f ba25b53 fd8f1df f3edf46 fd8f1df 3d57d8f fd8f1df 3d57d8f fd8f1df 3d57d8f 2e88a0b 3d57d8f 2e88a0b fd8f1df 2e88a0b f3edf46 3d57d8f ba25b53 fd8f1df f3edf46 fd8f1df 3d57d8f fd8f1df 3d57d8f ba25b53 fd8f1df 2e88a0b fd8f1df 3d57d8f fd8f1df 3d57d8f 2e88a0b fd8f1df 2e88a0b f3edf46 3d57d8f ba25b53 fd8f1df ba25b53 fd8f1df ba25b53 fd8f1df ba25b53 ba63f2d 5cdb81a ba63f2d fd8f1df 3d57d8f fd8f1df 3d57d8f fd8f1df 3d57d8f fd8f1df f3edf46 ba25b53 f3edf46 fd8f1df ba25b53 2e88a0b f3edf46 fd8f1df 2e88a0b fd8f1df 3d57d8f fd8f1df 3d57d8f fd8f1df 3d57d8f fd8f1df 3d57d8f ba25b53 f3edf46 2e88a0b f3edf46 ba25b53 f3edf46 2e88a0b fd8f1df 3d57d8f 2e88a0b fd8f1df 3d57d8f 2e88a0b ba25b53 2e88a0b f3edf46 fd8f1df 2e88a0b fd8f1df 3d57d8f ba25b53 fd8f1df 2e88a0b 3d57d8f ba25b53 2e88a0b ba63f2d fd8f1df 5cdb81a fd8f1df 2e88a0b fd8f1df 5cdb81a fd8f1df ba63f2d fd8f1df 2e88a0b fd8f1df 2e88a0b fd8f1df 3d57d8f 2e88a0b fd8f1df 2e88a0b fd8f1df 2e88a0b 3d57d8f ba25b53 5cdb81a 3d57d8f 2e88a0b fd8f1df ba25b53 fd8f1df ba25b53 2e88a0b ba25b53 fd8f1df ba25b53 fd8f1df ba25b53 2e88a0b ba25b53 fd8f1df 2e88a0b fd8f1df 2e88a0b ba25b53 3d57d8f fd8f1df ba25b53 2e88a0b fd8f1df 2e88a0b fd8f1df 2e88a0b fd8f1df baca1c4 fd8f1df baca1c4 2e88a0b ba25b53 2e88a0b ba63f2d 2e88a0b fd8f1df ba63f2d 6d0e312 fd8f1df ba25b53 2e88a0b 3d57d8f fd8f1df 3d57d8f ba25b53 3d57d8f ba63f2d |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 |
# -*- coding: utf-8 -*-
import streamlit as st
import random
import time
import math
# --- 페이지 설정 (스크립트 최상단) ---
# 이 부분이 가장 먼저 실행되어야 합니다!
st.set_page_config(layout="wide")
# --- 게임 설정 ---
MAX_YEARS = 5
QUARTERLY_ACTIONS_LIMIT = 1
# --- 난이도 설정 ---
DIFFICULTY_LEVELS = {
"쉬움": {
"initial_capital": 20,
"yearly_gain": 10,
"initial_approval_range": (50, 70),
"initial_congress_relations_range": (55, 75),
"initial_stability": 100,
"event_impact_modifier": 0.8,
"checks_balances_leniency": 0.15,
"vocab_level": "쉬움" # 어휘 수준 키
},
"보통": {
"initial_capital": 15,
"yearly_gain": 8,
"initial_approval_range": (40, 60),
"initial_congress_relations_range": (40, 60),
"initial_stability": 100,
"event_impact_modifier": 1.0,
"checks_balances_leniency": 0.0,
"vocab_level": "보통"
},
"어려움": {
"initial_capital": 10,
"yearly_gain": 6,
"initial_approval_range": (30, 50),
"initial_congress_relations_range": (25, 45),
"initial_stability": 90,
"event_impact_modifier": 1.2,
"checks_balances_leniency": -0.15,
"vocab_level": "어려움"
}
}
# --- 난이도별 텍스트 저장소 ---
# (이하 ALL_TEXTS 사전 정의는 이전 답변과 동일하게 유지됩니다)
ALL_TEXTS = {
# 대시보드 용어
"term_approval": {
"쉬움": "국민들이 얼마나 좋아하나 (인기 점수)",
"보통": "국정 지지도",
"어려움": "국정 운영에 대한 국민적 지지율"
},
"term_political_capital": {
"쉬움": "대통령 힘 점수 (정치력)",
"보통": "대통령 정치력",
"어려움": "대통령의 정책 추진 동력 (정치적 자본)"
},
"term_stability": {
"쉬움": "나라가 얼마나 안정적인가",
"보통": "국가 안정도",
"어려움": "국가 시스템 안정성 지수"
},
"term_congress_relations": {
"쉬움": "국회랑 얼마나 친한가",
"보통": "국회 관계 점수",
"어려움": "대정부 입법부 협력/견제 지수"
},
"term_policy_goals": {
"쉬움": "대통령 약속 진행 상황",
"보통": "주요 공약 달성률",
"어려움": "핵심 국정 과제 이행률"
},
# 기관 이름
"term_congress_name": {
"쉬움": "국회 (법 만드는 곳)",
"보통": "국회 (입법부)",
"어려움": "입법부 (국회)"
},
"term_judiciary_name": {
"쉬움": "법원 (법 지키는지 보는 곳)",
"보통": "법원/헌법재판소 (사법부)",
"어려움": "사법부 (법원 및 헌법재판소)"
},
# UI 제목/라벨
"dashboard_title": {
"쉬움": "📊 나라 상황판",
"보통": "📊 국정 현황판",
"어려움": "📊 국정 운영 대시보드"
},
"dashboard_term": {
"쉬움": "대통령 {year}년차, {quarter}번째 계절",
"보통": "임기 {year}년차 {quarter}분기",
"어려움": "대통령 임기 {year}년차 제 {quarter}사분기"
},
"institutions_title": {
"쉬움": "다른 중요 기관들 소식",
"보통": "기관 동향",
"어려움": "타 국가기관 현황 브리핑"
},
"cabinet_title": {
"쉬움": "장관님들 소식",
"보통": "내각 현황",
"어려움": "행정 각 부처 장관 동향"
},
"agenda_title": {
"쉬움": "📰 이번 계절 보고서",
"보통": "📰 분기별 보고",
"어려움": "📰 분기별 국정 현안 보고"
},
"agenda_event_title": {
"쉬움": "이번 계절의 일: {title}",
"보통": "현안/기회: {title}",
"어려움": "분기 현안 브리핑: {title}"
},
"briefing_title": {
"쉬움": "📝 비서실 도움말",
"보통": "📝 보좌진 브리핑 및 대응 방안",
"어려움": "📝 참모진 현안 분석 및 권고안"
},
"office_title": {
"쉬움": "🏛️ 대통령 방",
"보통": "🏛️ 대통령 집무실",
"어려움": "🏛️ 대통령 집무실" # 동일하게 사용
},
"office_available_capital": {
"쉬움": "쓸 수 있는 힘 점수: **{capital} P**",
"보통": "가용 정치력: **{capital} P**",
"어려움": "가용 정치적 자본: **{capital} P**"
},
"office_action_limit": {
"쉬움": "이번 계절에 할 수 있는 큰 결정: {remaining}번 남음",
"보통": "이번 분기 행동 가능 횟수: {remaining}회",
"어려움": "현 분기 주요 의사결정 잔여 횟수: {remaining}회"
},
"office_issue_actions": {
"쉬움": "지금 생긴 일 해결하기",
"보통": "현안 관련 조치",
"어려움": "당면 현안 대응 옵션"
},
"office_standard_actions": {
"쉬움": "평소에 할 수 있는 일들",
"보통": "상시 국정 활동",
"어려움": "상시 국정 운영 활동"
},
"term_event_log": {
"쉬움": "그동안 있었던 일들 기록",
"보통": "국정 운영 로그",
"어려움": "국정 운영 타임라인 기록"
},
"button_next_quarter": {
"쉬움": "➡️ 다음 계절로!",
"보통": "➡️ 다음 분기 진행",
"어려움": "➡️ 차기 분기 개시"
},
"button_restart": {
"쉬움": "다시 해볼래!",
"보통": "다시 시작하기",
"어려움": "게임 재시작"
},
# 이벤트 제목 예시
"event_title_minor_disaster": { "쉬움": "작은 재난 발생", "보통": "소규모 재난 발생", "어려움": "국지적 자연재해 발생"},
"event_desc_minor_disaster": { "쉬움": "비가 많이 오거나 불이 나서 동네 몇 군데가 피해를 입었어요. 빨리 도와줘야 해요.", "보통": "집중 호우 또는 산불 등으로 특정 지역에 피해가 발생했습니다. 신속한 지원이 필요합니다.", "어려움": "국지성 호우 또는 산불로 인해 일부 지역에서 물적 피해가 보고되었습니다. 긴급 구호 및 복구 계획 수립이 요구됩니다."},
"action_visit_disaster_area": { "쉬움": "피해 지역 가서 위로하기", "보통": "피해 지역 현장 방문 및 지원 약속", "어려움": "피해 지역 순방 및 정부 차원 지원 공표"},
"event_title_small_protest": { "쉬움": "사람들이 모여서 외치고 있어요", "보통": "소규모 시위 발생", "어려움": "제한적 규모의 집회 발생"},
"event_desc_small_protest": { "쉬움": "어떤 문제 때문에 몇몇 사람들이 모여 목소리를 내고 있어요. 시끄럽지만 큰 문제는 아니에요.", "보통": "특정 현안에 대한 불만으로 일부 시민들이 소규모 집회를 열고 있습니다. 상황 관리가 필요합니다.", "어려움": "특정 정책 또는 사회 현안에 대한 이견 표출로 소규모 집회가 개최되었습니다. 확산 가능성 주시 요망."},
"action_meet_protesters": { "쉬움": "시위대 이야기 들어보기", "보통": "시위대 대표 면담 추진", "어려움": "집회 주동자 대화 채널 구축 시도"},
"event_title_price_rise": {"쉬움": "물건값이 조금 올랐어요", "보통": "물가 소폭 상승", "어려움": "소비자 물가 지수 소폭 상승"},
"event_desc_price_rise": {"쉬움": "가게 물건값이 조금 올라서 사람들이 걱정해요. 안심시켜야 해요.", "보통": "일부 품목의 가격 상승으로 소비자 물가가 소폭 상승했습니다. 시장 불안 심리 차단이 필요합니다.", "어려움": "주요 소비재 가격 인상으로 인해 단기 인플레이션 압력이 감지됩니다. 선제적 시장 안정화 조치가 요구됩니다."},
"action_address_prices": {"쉬움": "물가 괜찮다고 말하기", "보통": "물가 안정 노력 관련 대국민 메시지 발표", "어려움": "물가 안정 기조 유지 관련 정부 입장 표명"},
"event_title_summit_offer": {"쉬움": "다른 나라 대통령 만나자고 해요", "보통": "정상회담 개최 제안", "어려움": "양자 정상회담 개최 제의 접수"},
"event_desc_summit_offer": {"쉬움": "이웃 나라 대통령이 만나자고 연락 왔어요. 친하게 지내면 좋아요.", "보통": "주요 동맹국/주변국 정상으로부터 양자 회담 개최 제안이 왔습니다. 외교적 성과 기회입니다.", "어려움": "주요 외교 파트너 국가 수반의 공식적인 양자 정상회담 제의가 접수되었습니다. 의제 조율 및 전략 수립이 필요합니다."},
"action_accept_summit": {"쉬움": "만나서 좋은 얘기하기", "보통": "정상회담 적극 수락 및 의제 준비 지시", "어려움": "정상회담 제의 수락 및 실무 준비 TF 구성 지시"},
"event_title_host_event": {"쉬움": "큰 국제 행사 우리나라에서 열까?", "보통": "대규모 국제 행사 유치 가능성", "어려움": "메가 이벤트 국내 유치 타당성 검토"},
"event_desc_host_event": {"쉬움": "올림픽 같은 큰 행사를 우리나라에서 열 수 있을지 알아보고 있대요. 좋겠죠?", "보통": "대규모 국제 스포츠/문화 행사 유치 경쟁에서 유리한 고지를 점했다는 보고입니다. 국가 이미지 제고 효과가 기대됩니다.", "어려움": "주요 국제 스포츠/문화 행사의 차기 개최지 선정과 관련하여 유력 후보로 부상했다는 분석입니다. 국가 브랜드 가치 제고 및 경제적 파급 효과가 예상됩니다."} ,
"action_support_hosting": {"쉬움": "행사 유치 돕기", "보통": "국제 행사 유치위원회에 예산/행정 지원 강화", "어려움": "범정부 차원의 국제 행사 유치 지원 체계 구축"},
"event_title_tech_investment": {"쉬움": "새로운 기술에 투자할까?", "보통": "신기술 분야 투자 적기 보고", "어려움": "미래 전략기술 분야 투자 최적 시점 분석"},
"event_desc_tech_investment": {"쉬움": "미래에 중요해질 새 기술에 투자하면 좋겠다는 보고예요. 돈 벌 수 있을까요?", "보통": "미래 산업 관련 핵심 신기술 분야에 대한 국내외 투자 환경이 유리하다는 분석입니다. 정부 지원 시 관련 공약 달성에 도움이 될 수 있습니다.", "어려움": "차세대 핵심 기술 분야의 R&D 및 상용화 투자 환경이 우호적으로 조성되었다는 분석 보고입니다. 국가 경쟁력 강화 및 관련 국정 과제 이행의 기회입니다."},
"action_review_tech_funding": {"쉬움": "새 기술 연구비 지원 알아보기", "보통": "신기술 분야 R&D 예산 증액 국회 요청 준비", "어려움": "전략기술 R&D 예산 확대 편성 및 입법부 설득 전략 수립"},
"event_title_opposition_criticism": {"쉬움": "야당이 {minister} 장관 싫어해요", "보통": "야당, {minister} 장관 책임론 제기", "어려움": "야당의 {minister} 장관에 대한 문책 요구"},
"event_desc_opposition_criticism": {"쉬움": "국회 반대편 의원들이 {minister} 장관이 일 못한다고 계속 비판해요.", "보통": "야당에서 {minister} 장관의 정책 실패 또는 부적절한 처신을 이유로 책임론을 강하게 제기하고 있습니다.", "어려움": "주요 야당은 {minister} 장관의 직무 수행 능력 및 정책적 판단에 심각한 문제가 있다며 사퇴 또는 해임 건의를 검토 중입니다."},
"action_defend_minister": {"쉬움": "{minister} 장관 편들어주기", "보통": "{minister} 장관 엄호 및 야당 설득 시도", "어려움": "{minister} 장관에 대한 신임 재확인 및 야당 공세 대응"},
"event_title_party_dissent": {"쉬움": "우리 편에서도 다른 소리가 나와요", "보통": "여당 내 특정 현안 관련 이견 노출", "어려움": "집권여당 내 정책 노선 관련 이견 표출"},
"event_desc_party_dissent": {"쉬움": "대통령이랑 같은 편 의원들 중에서도 정부가 하는 일에 대해 다른 생각을 말하기 시작했어요.", "보통": "정부 주요 정책 방향에 대해 여당 내 일부 의원들이 공개적으로 다른 목소리를 내기 시작했습니다. 내부 조율이 필요해 보입니다.", "어려움": "주요 국정 현안에 대한 당정 간 이견이 일부 언론을 통해 노출되었습니다. 당내 결속력 약화 및 정책 추진 동력 저하가 우려됩니다."},
"action_meet_party_members": {"쉬움": "우리 편 의원들 만나서 얘기하기", "보통": "여당 지도부/중진 의원들과 비공개 회동", "어려움": "여당 의원들과의 정책 간담회를 통한 내부 이견 조율"},
"event_title_media_criticism": {"쉬움": "신문/뉴스에서 정부 욕해요", "보통": "언론의 특정 정책 비판 보도", "어려움": "주요 언론의 정부 정책 비판 기조 강화"},
"event_desc_media_criticism": {"쉬움": "신문이나 뉴스에서 정부가 하는 일 하나를 콕 집어서 나쁘다고 말해요. 사람들이 믿으면 어쩌죠?", "보통": "주요 언론에서 정부의 특정 정책에 대한 문제점을 지적하거나 부정적인 여론을 부각하는 보도가 이어지고 있습니다.", "어려움": "복수의 주요 언론 매체에서 특정 정부 정책의 문제점 및 부작용을 집중적으로 보도하며 비판적 여론 형성을 주도하고 있습니다."},
"action_media_interview": {"쉬움": "기자들 만나서 설명하기", "보통": "언론 인터뷰를 통해 정책 설명 및 오해 해소", "어려움": "대통령 또는 주무 장관의 언론 브리핑을 통한 정책 정당성 설파"},
"event_title_congress_data_request": {"쉬움": "국회에서 자료 달래요", "보통": "국회, 정부 정책 자료 요구", "어려움": "국회의 행정부에 대한 자료 제출 요구"},
"event_desc_congress_data_request": {"쉬움": "국회의원들이 정부가 하는 일에 대해 자세히 알려달라고 자료를 내래요. 귀찮네요.", "보통": "국회 상임위원회에서 정부가 추진 중인 정책과 관련된 상세 자료 제출을 공식적으로 요구했습니다.", "어려움": "국회 소관 상임위원회에서 특정 정책의 추진 경과 및 예산 집행 내역 등 민감한 자료의 제출을 요구하여 행정부 부담이 가중되고 있습니다."},
"action_submit_data": {"쉬움": "자료 잘 만들어서 주기", "보통": "성실히 자료 제출 준비 지시", "어려움": "국회 요구 자료의 신속하고 정확한 제출 지시"},
"action_minimize_data": {"쉬움": "자료 조금만 주기 (혼날지도?)", "보통": "자료 제출 범위 최소화 검토 (관계 악화 위험)", "어려움": "제출 자료 범위에 대한 법적 검토 및 최소 수준 제출 추진"},
"event_title_congress_hearing_review": {"쉬움": "국회가 청문회 할지 고민 중이래요", "보통": "국회, 특정 사안 청문회 개최 검토", "어려움": "국회의 특정 현안 관련 청문회 개최 검토"},
"event_desc_congress_hearing_review": {"쉬움": "국회에서 시끄러운 문제에 대해 담당자 불러서 물어보는 청문회를 열지 말지 생각 중이래요. 열리면 골치 아파요.", "보통": "국회에서 논란이 되는 특정 사안에 대해 관계 부처 장관 등을 출석시켜 질의하는 청문회 개최 여부를 검토 중입니다. 개최 시 정부에 부담이 될 수 있습니다.", "어려움": "특정 국정 현안의 진상 규명 또는 책임 소재 파악을 위해 국회 차원의 청문회 개최 필요성이 제기되어 관련 상임위에서 논의가 진행 중입니다."},
"action_prevent_hearing": {"쉬움": "청문회 못 열게 막아보기", "보통": "여당 통해 청문회 개최 저지 또는 연기 시도", "어려움": "여당과 협력하여 청문회 개최의 부당성 설파 및 무산 유도"},
"action_prepare_hearing": {"쉬움": "청문회 잘 대답하게 준비하기", "보통": "청문회 대비 관계부처 합동 준비 지시", "어려움": "예상 쟁점 분석 및 대응 논리 개발 등 청문회 철저 대비 지시"},
"event_title_judiciary_lawsuit": {"쉬움": "누가 정부를 법원에 고소했어요", "보통": "법원, 정부 정책 관련 소송 접수", "어려움": "정부 정책 관련 행정소송 제기"},
"event_desc_judiciary_lawsuit": {"쉬움": "정부가 한 일이 잘못됐다면서 누가 법원에 고소했어요. 재판에서 지면 큰일나요.", "보통": "정부가 시행한 특정 정책의 위법 또는 부당성을 주장하며 시민 또는 단체가 행정소송을 제기했습니다. 판결 결과에 따라 정책 추진에 차질이 발생할 수 있습니다.", "어려움": "정부의 특정 행정 처분 또는 부작위에 대해 그 효력을 다투는 행정소송이 관할 법원에 접수되었습니다. 사법부의 판단에 따라 해당 정책의 지속 가능성이 결정될 수 있습니다."},
"action_prepare_legal_defense": {"쉬움": "변호사 시켜서 잘 싸우라고 하기", "보통": "법무부에 법적 대응 논리 개발 및 준비 지시", "어려움": "법무부 주관 하에 정부 소송대리인단 구성 및 적극적 변론 준비 지시"},
"action_persuade_public": {"쉬움": "국민들에게 우리 편 들어달라고 설득하기", "보통": "정책의 정당성 관련 대국민 홍보 강화", "어려움": "해당 정책의 공익성 및 불가피성에 대한 적극적 여론 형성 노력"},
"event_title_routine_report": { "쉬움": "이번 계절은 조용했어요", "보통": "일상 국정 보고", "어려움": "정기 국정 운영 현황 보고"},
"event_desc_routine_report": { "쉬움": "이번 계절은 별일 없었어요. 하던 일 계속하면 돼요.", "보통": "이번 분기는 특별한 현안 없이 국정이 비교적 안정적으로 운영되고 있다는 보고입니다. 기존 정책 추진에 집중할 시기입니다.", "어려움": "현 분기 특이사항 없이 안정적인 국정 운영 기조가 유지되고 있다는 정례 보고입니다. 기존 정책과제의 지속적인 추진이 권고됩니다."},
"action_check_goal_progress": { "쉬움": "약속 얼마나 지켰나 보기", "보통": "주요 공약 진행 상황 점검", "어려움": "핵심 국정 과제 이행 현황 중간 점검"},
"action_strengthen_pr": { "쉬움": "정부가 잘하고 있다고 자랑하기", "보통": "정책 홍보 활동 강화 지시", "어려움": "국정 운영 성과 홍보 전략 강화 지시"},
# 행동 관련 메시지
"action_button_label": { # 버튼 레이블은 통일성 위해 보통 수준 유지
"쉬움": "{action} ({cost} 힘 점수)",
"보통": "{action} ({cost}P)",
"어려움": "{action} (정치자본 {cost} 소모)"
},
"action_cost_warning": {
"쉬움": "힘 점수가 모자라요!",
"보통": "정치력이 부족합니다!",
"어려움": "정치적 자본이 부족합니다!"
},
"action_success_message": {
"쉬움": "'{action}' 하라고 시켰어요.",
"보통": "'{action}' 지시 완료.",
"어려움": "'{action}' 관련 대통령령 발령 완료." # 예시
},
"action_impact_approval": {
"쉬움": "국민 인기도 {change:+}%p 바뀔 듯",
"보통": "국정 지지도 {change:+}%p 변동 예상",
"어려움": "국민 지지율 {change:+}%p 변동 예측"
},
"action_impact_stability":{
"쉬움": "나라 안정 점수 {change:+}%p 바뀔 듯",
"보통": "국가 안정도 {change:+}%p 변동 예상",
"어려움": "국가 안정성 지수 {change:+}%p 변동 예측"
},
"action_impact_congress":{
"쉬움": "국회랑 관계 점수 {change:+}%p 바뀔 듯",
"보통": "국회 관계 점수 {change:+}%p 변동 예상",
"어려움": "대 국회 관계 지수 {change:+}%p 변동 예측"
},
"action_impact_goal":{
"쉬움": "약속 '{goal}' {prog:+}%p 만큼 진행될 듯",
"보통": "공약 '{goal}' {prog:+}%p 달성 예상",
"어려움": "국정 과제 '{goal}' {prog:+}%p 진척 예상"
},
# 연말 결산
"checks_balances_title": {
"쉬움": "{year}년차, 한 해 돌아보기",
"보통": "{year}년차 연말 국정 결산 (견제와 균형)",
"어려움": "{year}년차 연말 국정 평가 (기관 간 상호작용 분석)"
},
"checks_balances_bill_submitted": {
"쉬움": "[국회] '{bill_name}' 법안 내봤어요.",
"보통": "[국회] '{bill_name}' 법안 제출됨.",
"어려움": "[입법부] '{bill_name}' 의안 상정됨."
},
"checks_balances_bill_passed": {
"쉬움": "[국회] '{bill_name}' 법안 통과! 👍",
"보통": "[국회] '{bill_name}' 법안 통과! 🎉",
"어려움": "[입법부] '{bill_name}' 의안 가결됨."
},
"checks_balances_bill_failed": {
"쉬움": "[국회] '{bill_name}' 법안 통과 못했어요 😥",
"보통": "[국회] '{bill_name}' 법안 부결.",
"어려움": "[입법부] '{bill_name}' 의안 부결됨."
},
"checks_balances_investigation": {
"쉬움": "[국회] 국회가 정부 하는 일 자세히 살펴보겠대요. 귀찮아질かも...",
"보통": "[국회] 특정 사안에 대한 국정조사/청문회가 발동되었습니다. 정부 부담 증가.",
"어려움": "[입법부] 국회의 국정조사권 또는 청문회 발동. 행정부 운영 부담 가중."
},
"checks_balances_judiciary_ruling":{
"쉬움": "[법원] '{case_name}' 사건 판결: {result}",
"보통": "[사법부] '{case_name}' 사건 판결: {result}",
"어려움": "[사법부] '{case_name}' 사안에 대한 사법적 판단: {result}"
},
"checks_balances_judiciary_uphold":{
"쉬움": "문제 없대요!",
"보통": "문제 없음 (합헌/적법)",
"어려움": "합헌 또는 적법 결정"
},
"checks_balances_judiciary_strike_down":{
"쉬움": "문제 있대요! 계획 바꾸거나 조심해야 함.",
"보통": "문제 있음! (위헌/위법) 정책 차질 발생.",
"어려움": "위헌 또는 위법 결정. 관련 정책 추진 제동."
},
"checks_balances_summary":{
"쉬움": "{year}년차 끝! (법안 통과: {passed}개, 실패: {failed}개)",
"보통": "{year}년차 결산 완료 (법안 통과: {passed}건, 부결: {failed}건)",
"어려움": "{year}년차 국정 평가 완료 (법안 가결: {passed}건, 부결: {failed}건)"
},
# 게임 오버
"game_over_title": {
"쉬움": "대통령 임기 끝! (총 {years}년)",
"보통": "임기 종료 (총 {years}년)",
"어려움": "대통령 임기 만료 (재임 기간: {years}년)"
},
"game_over_subtitle": {
"쉬움": "내가 대통령으로 한 일 결과",
"보통": "최종 국정 운영 결과",
"어려움": "임기 만료 시점 국정 운영 평가"
},
# 용어 사전 (쉬움/보통/어려움 순서)
"glossary_veto": {
"쉬움": "**법률안 거부권:** 국회가 만든 법이 마음에 안 들 때, 대통령이 '다시 생각해 보세요!'라고 돌려보낼 수 있는 힘. 하지만 국회 의원들이 아주 많이(2/3 이상) 찬성하면 거부 못함.",
"보통": "**법률안 거부권 (재의 요구권):** 국회가 통과시킨 법률안에 대해 대통령이 이의가 있을 때, 공포하지 않고 국회에 다시 심의해달라고 요구할 수 있는 권한. 국회는 재적의원 과반수 출석과 출석의원 3분의 2 이상의 찬성으로 재의결 가능.",
"어려움": "**법률안 재의 요구권 (거부권):** 입법부가 의결한 법률안에 대해 행정부 수반인 대통령이 이의를 제기하며 공포를 거부하고 입법부에 재심의를 요구하는 헌법상 권한. 국회 재의결 요건(재적 과반 출석, 출석 2/3 찬성) 충족 시 법률로 확정."
},
"glossary_exec_order": {
"쉬움": "**대통령 명령:** 법을 잘 지키기 위해 대통령이 내리는 자세한 규칙. 법보다 높을 순 없음.",
"보통": "**행정명령 (대통령령):** 대통령이 법률에서 위임받은 사항이나 법률을 집행하기 위해 필요한 사항에 대해 발하는 명령. 법률의 범위 내에서만 유효.",
"어려움": "**대통령령 (집행명령/위임명령):** 헌법과 법률에 근거하여 대통령이 발하는 행정입법의 한 형태. 법률 집행을 위한 집행명령과 법률의 위임에 따른 위임명령으로 구분되며, 사법심사의 대상."
},
"glossary_budget_proposal": {
"쉬움": "**예산안 만들기:** 내년에 나라 살림을 어떻게 쓸지 정부(대통령)가 계획 짜서 국회에 내는 것.",
"보통": "**예산안 편성권:** 정부(대통령)가 다음 해의 국가 살림 계획인 예산안을 짜서 국회에 제출할 수 있는 권한.",
"어려움": "**정부의 예산안 편성권:** 행정부가 차기 회계연도의 국가 재정 운용 계획인 예산안을 수립하여 입법부에 제출하는 헌법상 권한."
},
"glossary_budget_approval": {
"쉬움": "**예산안 심사/확정:** 국회가 정부가 낸 예산안을 보고 '이렇게 써도 좋다!' 결정하는 것. 돈을 깎거나 (정부 동의 얻어) 늘릴 수 있음.",
"보통": "**국회 예산 심의/확정권:** 정부가 제출한 예산안을 국회가 심사하고 최종적으로 확정하는 권한. 정부 예산안을 삭감하거나 증액(정부 동의 필요)할 수 있음.",
"어려움": "**국회의 예산안 심의·확정권:** 정부가 제출한 예산안에 대해 국회가 심의를 거쳐 수정 또는 확정하는 권한. 재정 민주주의의 핵심 요소로, 감액은 자유로우나 증액 및 새 비목 설치는 정부 동의 필요."
},
"glossary_judicial_review_law": {
"쉬움": "**법률 심사권 (헌법재판소):** 국회가 만든 법이 가장 큰 법인 '헌법'에 어긋나는지 아닌지 헌법재판소가 판단하는 힘.",
"보통": "**위헌법률심판권:** 국회가 만든 법률이 헌법에 위배되는지 여부를 헌법재판소가 심판하는 권한.",
"어려움": "**위헌법률심판 제청권/심판권:** 법률의 위헌 여부가 재판의 전제가 된 경우 법원의 제청 또는 당사자의 신청에 의해 헌법재판소가 해당 법률의 헌법 합치성을 심판하는 권한."
},
"glossary_judicial_review_order":{
"쉬움": "**명령/규칙 심사권 (대법원):** 대통령 명령 같은 규칙이 헌법이나 법률에 맞는지 대법원이 최종 판단하는 힘.",
"보통": "**명령·규칙·처분 심사권:** 대통령령 등 행정기관이 만든 명령이나 규칙, 또는 행정 처분이 헌법이나 법률에 위반되는지 여부를 대법원이 최종적으로 심사하는 권한.",
"어려움": "**명령·규칙·처분에 대한 최종 심사권:** 행정입법(명령, 규칙)이나 행정처분의 위헌·위법성이 재판의 전제가 된 경우, 대법원이 이를 최종적으로 심사하여 판단하는 권한."
},
"glossary_parliamentary_audit":{
"쉬움": "**국정감사/조사:** 국회가 정부가 일 잘 하고 있는지, 잘못은 없는지 감시하고 조사하는 활동.",
"보통": "**국정감사/조사권:** 국회가 정부의 국정 운영 전반이나 특정 사안에 대해 잘못된 점이 없는지 감사하거나 조사할 수 있는 권한. 행정부를 견제하는 주요 수단.",
"어려움": "**국정감사 및 조사권:** 국회가 정부의 국정 수행 실태를 파악하고 감독하기 위해 매년 정기적으로(감사) 또는 특정 현안에 대해(조사) 행사하는 헌법상 권한."
},
"glossary_impeachment":{
"쉬움": "**탄핵:** 대통령 같은 높은 공무원이 아주 큰 잘못(헌법/법률 위반)을 했을 때, 국회가 그만두게 해달라고 헌법재판소에 요청하는 것.",
"보통": "**탄핵소추권:** 대통령 등 고위 공직자가 직무상 중대한 헌법이나 법률 위반 시, 국회가 파면을 결정해달라고 헌법재판소에 청구할 수 있는 권한.",
"어려움": "**탄핵소추 의결권 및 심판 청구권:** 국회가 헌법 또는 법률을 중대하게 위반한 특정 고위 공직자에 대해 파면을 구하는 소추안을 의결하고 헌법재판소에 심판을 청구하는 권한."
},
"glossary_confirmation_hearing":{
"쉬움": "**인사청문회:** 대통령이 뽑으려는 장관 같은 높은 공무원이 그 일 할 능력 있는지, 문제는 없는지 국회가 미리 살펴보는 자리.",
"보통": "**인사청문회:** 대통령이 임명하는 고위 공직 후보자에 대해 국회가 직무수행 능력과 도덕성 등을 검증하는 절차. 일부 직위는 국회 동의가 필수적.",
"어려움": "**인사청문회 실시권:** 대통령이 임명하는 주요 공직 후보자의 자질, 능력, 도덕성 등을 국회 상임위원회 또는 특별위원회 차원에서 검증하는 제도. 임명동의안 처리 또는 임명 전 검증 절차로 기능."
},
"glossary_political_capital":{
"쉬움": "**정치력 (게임 점수):** 대통령이 자기 뜻대로 일을 할 때 쓰는 힘 점수. 중요한 결정 할 때 필요.",
"보통": "**정치력 (Political Capital):** 대통령이 자신의 정책을 추진하거나 영향력을 행사하는 데 사용할 수 있는 비공식적인 자원. 지지도, 협상력, 리더십 등을 포함하는 개념으로 게임 내 행동력으로 사용됨.",
"어려움": "**정치적 자본 (Political Capital):** 대통령이 국정 운영 목표 달성을 위해 동원할 수 있는 무형의 자산. 국민적 지지, 의회 내 영향력, 정책 정당성, 리더십 등을 포괄하며, 정치적 행위의 비용으로 소모됨."
},
# 상시 행동 키
"action_replace_minister": {"쉬움": "{minister} 장관 바꾸기", "보통": "{minister} 장관 교체", "어려움": "{minister} 장관 경질"},
"action_prepare_bill": {"쉬움": "새로운 법 만들기 준비", "보통": "법률안 국회 제출 준비", "어려움": "입법안 발의 준비 착수"},
"action_meet_ruling_party": {"쉬움": "우리 편 의원들이랑 밥 먹기", "보통": "여당 지도부와 만찬 회동", "어려움": "집권여당 지도부와 정례 회동"},
"action_meet_opposition": {"쉬움": "반대편 의원들 만나보기", "보통": "야당 지도부 만나기 (관계 개선 시도)", "어려움": "주요 야당 지도부와 협의 채널 구축"},
"action_meet_biz_leaders": {"쉬움": "회사 사장님들 만나기", "보통": "주요 경제 단체 간담회", "어려움": "주요 경제계 인사 초청 간담회"},
"action_respect_judiciary": {"쉬움": "법원 존중한다고 말하기", "보통": "사법부 존중 메시지 발표", "어려움": "사법부 독립성 존중 관련 대국민 메시지 발표"},
"action_no_special_action": {"쉬움": "이번엔 그냥 넘어가기", "보통": "이번 분기 특별 조치 없음", "어려움": "현 분기 특이 조치 보류"},
# 상태 메시지 키
"status_loading_report": {"쉬움": "{year}년 {quarter}번째 계절 보고서 만드는 중...", "보통": "{year}년 {quarter}분기 보고 준비 중...", "어려움": "{year}년 제 {quarter}사분기 국정 현안 브리핑 준비 중..."} ,
"status_waiting_report": {"쉬움": "다음 계절 보고서 기다리는 중...", "보통": "다음 분기 보고를 기다리고 있습니다.", "어려움": "차기 분기 보고 대기 중..."} ,
"status_action_limit_reached": {"쉬움": "이번 계절({quarter})에 할 큰 결정 다 했어요. 다음 계절로 넘어가요.", "보통": "이번 분기({quarter}Q) 주요 활동을 완료했습니다. 다음 분기로 넘어가세요.", "어려움": "현 분기({quarter}Q) 주요 의사결정 한도를 소진했습니다. 차기 분기로 진행하십시오."},
# 사이드바 키
"sidebar_title": {"쉬움": "메뉴", "보통": "사이드 메뉴", "어려움": "부가 기능 메뉴"},
"sidebar_glossary_title": {"쉬움": "🏛️ 어려운 말 사전", "보통": "🏛️ 정부 용어 사전", "어려움": "🏛️ 주요 정부 용어 해설"},
# 기타 UI 텍스트
"error_invalid_action": {"쉬움": "잘못 골랐어요!", "보통": "잘못된 행동 선택입니다.", "어려움": "유효하지 않은 의사결정입니다."},
"error_event_generation_failed": {"쉬움": "보고서 만드는데 실패했어요 ㅠㅠ", "보통": "분기 보고 생성에 실패했습니다.", "어려움": "분기 현안 브리핑 생성 중 오류가 발생했습니다."},
"warning_briefing_loading": {"쉬움": "도움말 가져오는 중...", "보통": "브리핑 정보를 불러오는 중입니다.", "어려움": "참모진 권고안 로딩 중..."},
"caption_no_goals": {"쉬움": "정한 약속이 없어요.", "보통": "설정된 공약이 없습니다.", "어려움": "설정된 국정 과제가 없습니다."},
"caption_no_cabinet": {"쉬움": "장관님들 정보가 없어요.", "보통": "내각 정보가 없습니다.", "어려움": "행정 각료 정보가 없습니다."},
"caption_no_pending_bills": {"쉬움": "국회에서 지금 얘기 중인 법안이 없어요.", "보통": "현재 특별히 논의 중인 법안 없음", "어려움": "현재 계류 중인 주요 의안 없음"},
"caption_no_pending_cases": {"쉬움": "법원에서 지금 다루는 특별한 사건이 없어요.", "보통": "현재 특별한 사건 없음", "어려움": "현재 계류 중인 주요 사법 사건 없음"},
"label_pending_bills": {"쉬움": "국회에서 얘기 중인 법안들", "보통": "논의 중인 법안", "어려움": "계류 의안 목록"},
"label_pending_cases": {"쉬움": "법원에서 다루는 사건들", "보통": "진행 중인 주요 사건", "어려움": "계류 사법 사건 목록"},
"log_initial_approval": {"쉬움": "처음 인기도", "보통": "초기 국정 지지도", "어려움": "임기 개시 시점 지지율"},
"log_initial_goals": {"쉬움": "내가 하기로 한 약속들", "보통": "선택된 주요 공약", "어려움": "선정된 핵심 국정 과제"},
"log_action_result": {"쉬움": "결과:", "보통": "결과:", "어려움": "파급 효과:"},
"log_minister_change": {"쉬움": "{minister} 장관 바꿈.", "보통": "{minister} 장관 교체.", "어려움": "{minister} 장관 경질."},
"log_minister_old_approval": {"쉬움": "전 장관 인기도", "보통": "구 장관 지지도", "어려움": "전임 장관 지지율"},
"log_minister_new_approval": {"쉬움": "새 장관 인기도", "보통": "신 장관 지지도", "어려움": "신임 장관 지지율"},
"log_bill_prep_start": {"쉬움": "'{bill_name}' 법 만들기 시작.", "보통": "'{bill_name}' 제출 준비 착수.", "어려움": "'{bill_name}' 입법 준비 개시."},
"log_yearly_capital_gain": {"쉬움": "연말 보너스 힘 점수", "보통": "연말 보너스 정치력", "어려움": "연말 정치적 자본 회복"},
"log_end_of_term": {"쉬움": "--- 대통령 임기 끝! ---", "보통": "--- 임기 종료 ---", "어려움": "--- 대통령 임기 만료 ---"},
"log_start_year": {"쉬움": "--- {year}년차 시작! ---", "보통": "--- {year}년차 시작 ---", "어려움": "--- 임기 {year}년차 개시 ---"},
"log_start_quarter": {"쉬움": "--- {year}년차 {quarter}번째 계절 시작! ---", "보통": "--- {year}년차 {quarter}분기 시작 ---", "어려움": "--- {year}년차 제 {quarter}사분기 개시 ---"},
"log_error_minister_not_found": {"쉬움": "오류: {minister} 부서 못 찾음.", "보통": "오류: {minister} 부처를 찾을 수 없습니다.", "어려움": "오류: {minister} 부처 정보 부재."},
"log_bill_pass_chance": {"쉬움": "'{bill_name}' 통과 가능성", "보통": "'{bill_name}' 통과 확률", "어려움": "'{bill_name}' 가결 확률"},
"log_judiciary_case_received": {"쉬움": "[{judiciary_name}] '{case_name}' 사건 들어옴.", "보통": "[{judiciary_name}] '{case_name}' 사건 접수됨.", "어려움": "[{judiciary_name}] '{case_name}' 사안 계류됨."},
"log_judiciary_strike_down_chance": {"쉬움": "'{case_name}' 문제 있다고 판결할 가능성", "보통": "'{case_name}' 위헌/위법 판결 확률", "어려움": "'{case_name}' 위헌/위법 결정 확률"},
"log_natural_fluctuation": {"쉬움": "연말 점수 변화", "보통": "연말 자연 변동", "어려움": "연말 지표 자연 변동성"},
}
# --- 어휘 조정 함수 (난이도별 텍스트 반환) ---
def get_text(key, level="보통"):
"""난이도에 따라 다른 텍스트 반환, 없으면 '보통' -> key 순으로 fallback"""
level_texts = ALL_TEXTS.get(key)
if level_texts:
if level in level_texts:
return level_texts[level]
elif "보통" in level_texts:
# st.warning(f"텍스트 키 '{key}'에 대한 '{level}' 난이도 텍스트 없음. '보통' 사용.") # 디버깅용
return level_texts["보통"]
# st.error(f"텍스트 키 '{key}' 찾을 수 없음!") # 디버깅용
return key # 키 자체를 반환 (최후 fallback)
# --- 정부 및 다른 기관 초기화 함수 ---
def initialize_government(difficulty):
"""대통령 임기 시작 시 상태 및 다른 기관 정보 초기화 (난이도 적용)"""
settings = DIFFICULTY_LEVELS[difficulty]
vocab_level = settings["vocab_level"]
get_text_func = lambda k: get_text(k, vocab_level) # 초기화 시점 어휘 수준 적용
policy_options = ["경제 성장률 5% 달성", "청년 실업률 5% 이하 달성", "부동산 시장 안정화", "미래 산업 육성", "외교 관계 개선", "국방력 강화", "복지 예산 10% 증액", "탄소 배출 감축"]
selected_goals = random.sample(policy_options, 3)
initial_goals = {goal: 0 for goal in selected_goals}
total_seats = 300
gov_party_seats_base = random.randint(120, 170)
if difficulty == "어려움": gov_party_seats = min(gov_party_seats_base, random.randint(100, 155))
elif difficulty == "쉬움": gov_party_seats = max(gov_party_seats_base, random.randint(145, 180))
else: gov_party_seats = gov_party_seats_base
opp_party_seats = total_seats - gov_party_seats - random.randint(5, 15)
other_seats = total_seats - gov_party_seats - opp_party_seats
return {
'presidency_status': {
'approval_rating': random.randint(*settings['initial_approval_range']),
'political_capital': settings['initial_capital'],
'policy_goals': initial_goals,
'stability': settings['initial_stability'],
'congress_relations': random.randint(*settings['initial_congress_relations_range'])
},
'other_branches': {
'Congress': {
'name': get_text_func("term_congress_name"),
'seats': {'Government': gov_party_seats, 'Opposition': opp_party_seats, 'Others': other_seats},
'stance': '사안별 협조 및 견제' if difficulty == "보통" else ("대체로 협조적" if difficulty == "쉬움" else "대체로 비판적/견제"),
'key_focus': random.sample(['민생 안정', '정부 견제', '개혁 입법', '지역 예산 확보'], 2),
'pending_bills': [],
'relations_score': random.randint(*settings['initial_congress_relations_range'])
},
'Judiciary': {
'name': get_text_func("term_judiciary_name"),
'status': '특별한 사건 없음',
'independence_level': random.randint(70, 95) if difficulty != "어려움" else random.randint(80, 100),
'pending_cases': [],
'stance_modifier': 0
}
},
'cabinet': {
'경제부': {'minister_approval': random.randint(30,70), 'performance': 50},
'사회부': {'minister_approval': random.randint(30,70), 'performance': 50},
'외교부': {'minister_approval': random.randint(30,70), 'performance': 50},
'국방부': {'minister_approval': random.randint(30,70), 'performance': 50},
},
'event_log': [f"제 {random.randint(19, 22)}대 대통령 임기 시작 ({difficulty})"]
}
# --- 세션 상태 초기화 ---
if 'game_started' not in st.session_state:
st.session_state['game_started'] = False
if 'difficulty' not in st.session_state:
st.session_state['difficulty'] = "보통" # 기본값
# --- 난이도 선택 화면 ---
if not st.session_state.game_started:
st.title("👑 대통령의 균형추: 삼권분립 리더십 시뮬레이션")
st.write("게임을 시작하기 전에 난이도를 선택해주세요. 난이도에 따라 게임 설명과 용어가 달라집니다.")
selected_difficulty = st.radio(
"게임 난이도를 선택하세요:",
list(DIFFICULTY_LEVELS.keys()),
index=1,
horizontal=True,
captions=["쉬운 단어와 설명으로 게임해요.", "표준적인 설명으로 게임해요.", "전문적인 용어와 설명으로 게임해요."]
)
if st.button("게임 시작!"):
st.session_state['difficulty'] = selected_difficulty
st.session_state['game_year'] = 1
st.session_state['game_quarter'] = 1
st.session_state['game_state'] = initialize_government(selected_difficulty)
# 초기 로그 메시지 추가 (난이도별 텍스트 사용)
vocab_level_init = DIFFICULTY_LEVELS[selected_difficulty]["vocab_level"] # 여기에서 vocab_level 정의
get_text_func_init = lambda k: get_text(k, vocab_level_init) # 초기화용 get_text 함수
initial_approval = st.session_state.game_state['presidency_status']['approval_rating']
st.session_state.game_state['event_log'].append(f"{get_text_func_init('log_initial_approval')}: {initial_approval}%")
goals = st.session_state.game_state['presidency_status']['policy_goals'].keys()
st.session_state.game_state['event_log'].append(f"{get_text_func_init('log_initial_goals')}: {', '.join(goals)}")
st.session_state['current_quarterly_event'] = None
st.session_state['event_briefing'] = None
st.session_state['game_over'] = False
st.session_state['actions_taken_this_quarter'] = 0
st.session_state['game_started'] = True
st.rerun()
st.stop()
# --- 게임 진행 중 ---
# 현재 게임 난이도 및 어휘 수준 가져오기
difficulty = st.session_state.difficulty
settings = DIFFICULTY_LEVELS[difficulty]
vocab_level = settings["vocab_level"]
get_text_func = lambda key: get_text(key, vocab_level) # 게임 전반에 사용할 get_text 함수
# --- 분기별 현안/기회 생성 함수 ---
def generate_quarterly_event(year, quarter, game_state, difficulty):
"""매 분기 발생하는 현안/기회 생성 (난이도 및 견제 요소 반영)"""
settings = DIFFICULTY_LEVELS[difficulty]
vocab_level = settings["vocab_level"]
get_text_for_event = lambda k: get_text(k, vocab_level) # 이벤트 생성 시 사용할 get_text
presidency_status = game_state['presidency_status']
congress = game_state['other_branches']['Congress']
judiciary = game_state['other_branches']['Judiciary']
event_types = ["minor_issue", "opportunity", "political_move", "checks_balances_event", "random"]
weights = [30, 25, 20, 15, 10]
if difficulty == "어려움": weights = [35, 15, 25, 20, 5]
elif difficulty == "쉬움": weights = [20, 35, 15, 10, 20]
if congress['relations_score'] < 40:
weights[3] += 10
weights[2] += 5
chosen_type = random.choices(event_types, weights=weights, k=1)[0]
event = {"title_key": "", "description_key": "", "type": chosen_type, "options": [], "context": {}} # 키로 저장, context 추가
# 이벤트 내용 정의 (키 사용)
if chosen_type == "minor_issue":
issues = [
{"title_key": "event_title_minor_disaster", "desc_key": "event_desc_minor_disaster"},
{"title_key": "event_title_small_protest", "desc_key": "event_desc_small_protest"},
{"title_key": "event_title_price_rise", "desc_key": "event_desc_price_rise"},
]
selected = random.choice(issues)
event["title_key"] = selected["title_key"]
event["description_key"] = selected["desc_key"]
event["options"] = [
{"action_key": "action_visit_disaster_area", "cost": math.ceil(2 * (1/settings['event_impact_modifier'])), "impact": {"approval": random.randint(1,3), "stability": random.randint(0,2)}},
{"action_key": "action_meet_protesters", "cost": math.ceil(1 * (1/settings['event_impact_modifier'])), "impact": {"stability": random.randint(0,1)}},
{"action_key": "action_address_prices", "cost": math.ceil(1 * (1/settings['event_impact_modifier'])), "impact": {"approval": random.randint(-1,2)}},
]
elif chosen_type == "opportunity":
opps = [
{"title_key": "event_title_summit_offer", "desc_key": "event_desc_summit_offer"},
{"title_key": "event_title_host_event", "desc_key": "event_desc_host_event"},
{"title_key": "event_title_tech_investment", "desc_key": "event_desc_tech_investment"},
]
selected = random.choice(opps)
event["title_key"] = selected["title_key"]
event["description_key"] = selected["desc_key"]
event["options"] = [
{"action_key": "action_accept_summit", "cost": math.ceil(2 * (1/settings['event_impact_modifier'])), "impact": {"approval": random.randint(0,2), "policy_goals": {"외교 관계 개선": random.randint(1,3)}}}, # 공약 이름은 일단 하드코딩 유지
{"action_key": "action_support_hosting", "cost": math.ceil(3 * (1/settings['event_impact_modifier'])), "impact": {"stability": random.randint(1,2), "approval": random.randint(1,3)}},
{"action_key": "action_review_tech_funding", "cost": math.ceil(2 * (1/settings['event_impact_modifier'])), "impact": {"policy_goals": {"미래 산업 육성": random.randint(2,5)}}},
]
elif chosen_type == "political_move":
minister_name = random.choice(list(game_state['cabinet'].keys())) if game_state['cabinet'] else "특정"
event["context"]["minister"] = minister_name # context에 장관 이름 저장
moves = [
{"title_key": "event_title_opposition_criticism", "desc_key": "event_desc_opposition_criticism"},
{"title_key": "event_title_party_dissent", "desc_key": "event_desc_party_dissent"},
{"title_key": "event_title_media_criticism", "desc_key": "event_desc_media_criticism"},
]
selected = random.choice(moves)
event["title_key"] = selected["title_key"]
event["description_key"] = selected["desc_key"]
# 옵션에도 context 전달 필요 시 추가
event["options"] = [
{"action_key": "action_defend_minister", "cost": math.ceil(2 * (1/settings['event_impact_modifier'])), "impact": {"congress_relations": random.randint(-4,1)}, "context": {"minister": minister_name}},
{"action_key": "action_meet_party_members", "cost": math.ceil(1 * (1/settings['event_impact_modifier'])), "impact": {"political_capital": 1}},
{"action_key": "action_media_interview", "cost": math.ceil(1 * (1/settings['event_impact_modifier'])), "impact": {"approval": random.randint(-2,2)}},
]
elif chosen_type == "checks_balances_event":
cb_events = []
if congress['relations_score'] < 50 or difficulty == "어려움":
cb_events.append({"title_key": "event_title_congress_data_request", "desc_key": "event_desc_congress_data_request", "options": [{"action_key": "action_submit_data", "cost": 1, "impact": {"congress_relations": random.randint(0,2)}}, {"action_key": "action_minimize_data", "cost": 0, "impact": {"congress_relations": random.randint(-3,0)}}]})
cb_events.append({"title_key": "event_title_congress_hearing_review", "desc_key": "event_desc_congress_hearing_review", "options": [{"action_key": "action_prevent_hearing", "cost": 2, "impact": {"congress_relations": random.randint(-2,1)}}, {"action_key": "action_prepare_hearing", "cost": 1, "impact": {"stability": random.randint(-2,0)}}]})
if judiciary['independence_level'] > 80 or random.random() < 0.1:
cb_events.append({"title_key": "event_title_judiciary_lawsuit", "desc_key": "event_desc_judiciary_lawsuit", "options": [{"action_key": "action_prepare_legal_defense", "cost": 1, "impact": {}}, {"action_key": "action_persuade_public", "cost": 1, "impact": {"approval": random.randint(0,1)}}]})
if not cb_events:
chosen_type = "random" # 대체
else:
selected = random.choice(cb_events)
event["title_key"] = selected["title_key"]
event["description_key"] = selected["desc_key"]
event["options"] = selected["options"]
event["type"] = "checks_balances_event" # 타입 명시
# Random 타입 처리
if chosen_type == "random":
event["title_key"] = "event_title_routine_report"
event["description_key"] = "event_desc_routine_report"
event["type"] = "random"
event["options"] = [
{"action_key": "action_check_goal_progress", "cost": math.ceil(1 * (1/settings['event_impact_modifier'])), "impact": {}},
{"action_key": "action_strengthen_pr", "cost": math.ceil(1 * (1/settings['event_impact_modifier'])), "impact": {"approval": random.randint(0,1)}},
]
# 공통 옵션 추가 (키 사용)
event["options"].append({"action_key": "action_no_special_action", "cost": 0, "impact": {}})
# 최종 이벤트 객체 생성 (텍스트 변환)
final_event = {
"title": get_text_for_event(event["title_key"]).format(**event.get("context", {})),
"description": get_text_for_event(event["description_key"]).format(**event.get("context", {})),
"type": event["type"],
"options": []
}
for opt in event.get("options", []): # options가 없을 수도 있으므로 get 사용
final_event["options"].append({
"action": get_text_for_event(opt["action_key"]).format(**opt.get("context", {})),
"cost": opt["cost"],
"impact": opt.get("impact", {}) # impact가 없을 수도 있음
})
log_message = f"{year}년차 {quarter}분기 현안: {final_event['title']}"
if 'event_log' in game_state:
game_state['event_log'].append(log_message)
return final_event
# --- 이벤트 브리핑 제공 함수 ---
def provide_event_briefing(event, game_state, difficulty):
"""분기별 현안/기회에 대한 브리핑 생성 (난이도별 어휘 적용)"""
settings = DIFFICULTY_LEVELS[difficulty]
vocab_level = settings["vocab_level"]
get_text_for_briefing = lambda key: get_text(key, vocab_level) # 브리핑용 get_text
title = event['title'] # 이미 변환된 텍스트 사용
briefing = f"**{get_text_for_briefing('dashboard_term').format(year=st.session_state['game_year'], quarter=st.session_state['game_quarter'])} 보고: {title}**\n\n"
briefing += f"{event['description']}\n\n" # 이미 변환된 텍스트 사용
briefing += f"**{get_text_for_briefing('briefing_title')}**\n" # 브리핑 제목도 난이도별로
options_data = []
for i, opt in enumerate(event.get("options", [])):
cost = opt['cost']
action_text = opt['action'] # 이미 변환된 텍스트 사용
briefing += f"{i+1}. **{action_text}** ({get_text_for_briefing('action_button_label').format(action='', cost=cost)})\n"
effects = []
impact_modifier = settings['event_impact_modifier']
impact = opt.get("impact", {}) # impact가 없을 수도 있음
if 'approval' in impact:
val = impact['approval']
adjusted_val = math.ceil(val / impact_modifier) if val < 0 else math.floor(val * impact_modifier)
effects.append(get_text_for_briefing('action_impact_approval').format(change=adjusted_val))
if 'stability' in impact:
val = impact['stability']
adjusted_val = math.ceil(val / impact_modifier) if val < 0 else math.floor(val * impact_modifier)
effects.append(get_text_for_briefing('action_impact_stability').format(change=adjusted_val))
if 'congress_relations' in impact:
val = impact['congress_relations']
adjusted_val = math.ceil(val / impact_modifier) if val < 0 else math.floor(val * impact_modifier)
effects.append(get_text_for_briefing('action_impact_congress').format(change=adjusted_val))
if 'policy_goals' in impact:
for goal, prog in impact['policy_goals'].items():
if goal in game_state['presidency_status']['policy_goals']:
val = prog
adjusted_val = math.ceil(val / impact_modifier) if val < 0 else math.floor(val * impact_modifier)
effects.append(get_text_for_briefing('action_impact_goal').format(goal=goal, prog=adjusted_val))
if effects: briefing += f" - *예상 결과:* {', '.join(effects)}\n"
options_data.append(opt)
return {"text": briefing, "options": options_data}
# --- 대통령 행동 실행 함수 ---
def execute_presidential_action(action_index, briefing_data, game_state, difficulty):
"""선택된 대통령 행동 실행 (난이도 영향 적용)"""
settings = DIFFICULTY_LEVELS[difficulty]
impact_modifier = settings['event_impact_modifier']
vocab_level = settings["vocab_level"]
get_text_for_exec = lambda key: get_text(key, vocab_level) # 실행 시 사용할 get_text
options = briefing_data.get("options", [])
if not (0 <= action_index < len(options)):
st.error(get_text_for_exec("error_invalid_action"))
return False
selected_action = options[action_index]
action_name = selected_action['action'] # 이미 변환된 텍스트
cost = selected_action['cost']
impact = selected_action.get('impact', {})
presidency_status = game_state['presidency_status']
other_branches = game_state['other_branches']
cabinet = game_state.get('cabinet', {}) # cabinet이 없을 경우 대비
if presidency_status['political_capital'] < cost:
st.warning(get_text_for_exec('action_cost_warning') + f" (필요: {cost}, 보유: {presidency_status['political_capital']})")
return False
presidency_status['political_capital'] -= cost
log_message = f" - 대통령 지시({st.session_state.game_quarter}Q): '{action_name}' 수행 ({get_text_for_exec('term_political_capital')} {cost} 소모)"
game_state['event_log'].append(log_message)
st.success(get_text_for_exec('action_success_message').format(action=action_name))
approval_change_log = 0
stability_change_log = 0
congress_change_log = 0
if 'approval' in impact:
change = impact['approval']
adjusted_change = math.ceil(change / impact_modifier) if change < 0 else math.floor(change * impact_modifier)
presidency_status['approval_rating'] += adjusted_change
approval_change_log = adjusted_change
if 'stability' in impact:
change = impact['stability']
adjusted_change = math.ceil(change / impact_modifier) if change < 0 else math.floor(change * impact_modifier)
presidency_status['stability'] += adjusted_change
stability_change_log = adjusted_change
if 'congress_relations' in impact:
change = impact['congress_relations']
adjusted_change = math.ceil(change / impact_modifier) if change < 0 else math.floor(change * impact_modifier)
other_branches['Congress']['relations_score'] += adjusted_change
congress_change_log = adjusted_change
log_parts = []
if approval_change_log != 0: log_parts.append(f"{get_text_for_exec('term_approval')} {approval_change_log:+}%p")
if stability_change_log != 0: log_parts.append(f"{get_text_for_exec('term_stability')} {stability_change_log:+}%p")
if congress_change_log != 0: log_parts.append(f"{get_text_for_exec('term_congress_relations')} {congress_change_log:+}%p")
if log_parts: game_state['event_log'].append(f" - {get_text_for_exec('log_action_result')} {', '.join(log_parts)} 변동.")
if 'policy_goals' in impact:
for goal, progress in impact['policy_goals'].items():
if goal in presidency_status['policy_goals']:
change = progress
adjusted_change = math.ceil(change / impact_modifier) if change < 0 else math.floor(change * impact_modifier)
current_progress = presidency_status['policy_goals'][goal]
new_progress = min(100, current_progress + adjusted_change)
presidency_status['policy_goals'][goal] = new_progress
if new_progress > current_progress:
game_state['event_log'].append(f" - {get_text_for_exec('term_policy_goals')} '{goal}' {adjusted_change}%p 상승 (현재 {new_progress}%)")
# 장관 교체 로직 수정: 키를 기반으로 비교
minister_replace_action_key = "action_replace_minister"
minister_replace_action_text_template = get_text(minister_replace_action_key, vocab_level)
# action_name이 "어떤장관 장관 교체" 형태인지 확인
if minister_replace_action_text_template.startswith('{minister}') and \
minister_replace_action_text_template.endswith(action_name.split(' ')[-1]): # "장관 바꾸기", "장관 교체", "장관 경질" 등 끝부분 일치 확인
minister_to_replace = action_name.replace(minister_replace_action_text_template.split(' ')[-1], '').strip() # 끝부분 제거하고 이름 추출
if minister_to_replace in cabinet:
old_approval = cabinet[minister_to_replace]['minister_approval']
cabinet[minister_to_replace]['minister_approval'] = random.randint(40, 70)
cabinet[minister_to_replace]['performance'] = 50
approval_change = random.randint(-4, 4) if difficulty == "어려움" else (random.randint(-2, 5) if difficulty == "쉬움" else random.randint(-3, 3))
congress_change = random.randint(-6, 1) if difficulty == "어려움" else (random.randint(-3, 4) if difficulty == "쉬움" else random.randint(-5, 2))
presidency_status['approval_rating'] += approval_change
other_branches['Congress']['relations_score'] += congress_change
game_state['event_log'].append(f" - {get_text_for_exec('log_minister_change').format(minister=minister_to_replace)} ({get_text_for_exec('log_minister_old_approval')}: {old_approval}%, {get_text_for_exec('log_minister_new_approval')}: {cabinet[minister_to_replace]['minister_approval']}%)")
if approval_change != 0: game_state['event_log'].append(f" - {get_text_for_exec('term_approval')} {approval_change:+} 변동.")
if congress_change != 0: game_state['event_log'].append(f" - {get_text_for_exec('term_congress_relations')} {congress_change:+} 변동.")
elif minister_to_replace: # 이름이 추출되었는데 cabinet에 없는 경우
game_state['event_log'].append(get_text_for_exec('log_error_minister_not_found').format(minister=minister_to_replace))
# 이름 추출 실패 시 (예: action_name 형식이 예상과 다를 때) 별도 처리 없음
# 법안 제출 준비 로직 수정: 키를 기반으로 비교
bill_prep_action_key = "action_prepare_bill"
if action_name == get_text(bill_prep_action_key, vocab_level):
bill_name = f"{st.session_state['game_year']}년-{random.choice(['경제','사회','환경','외교'])}-법안" # 예시 이름
game_state['pending_legislation'] = game_state.get('pending_legislation', [])
game_state['pending_legislation'].append({"name": bill_name, "status": "준비중", "goal_related": random.choice(list(presidency_status['policy_goals'].keys())) if presidency_status['policy_goals'] else None})
game_state['event_log'].append(f" - {get_text_for_exec('log_bill_prep_start').format(bill_name=bill_name)}")
# 지표 범위 유지
presidency_status['approval_rating'] = max(0, min(100, presidency_status['approval_rating']))
presidency_status['stability'] = max(0, min(100, presidency_status['stability']))
other_branches['Congress']['relations_score'] = max(0, min(100, other_branches['Congress']['relations_score']))
if presidency_status.get('policy_goals'): # policy_goals가 없을 경우 대비
for goal in presidency_status['policy_goals']:
presidency_status['policy_goals'][goal] = max(0, min(100, presidency_status['policy_goals'][goal]))
st.session_state['actions_taken_this_quarter'] += 1
return True
# --- 견제와 균형 시뮬레이션 함수 ---
def simulate_checks_and_balances(game_state, difficulty):
"""(연말 실행) 국회와 사법부 반응 시뮬레이션 (난이도 적용 및 텍스트 사용)"""
settings = DIFFICULTY_LEVELS[difficulty]
vocab_level = settings["vocab_level"]
get_text_for_sim = lambda key: get_text(key, vocab_level) # 시뮬레이션용 get_text
presidency_status = game_state['presidency_status']
congress = game_state['other_branches']['Congress']
judiciary = game_state['other_branches']['Judiciary']
log = game_state['event_log']
year = st.session_state['game_year']
log.append(f"--- {get_text_for_sim('checks_balances_title').format(year=year)} ---")
# 1. 국회 시뮬레이션
if 'pending_legislation' in game_state:
for bill_to_submit in game_state['pending_legislation']:
if bill_to_submit['status'] == '준비중':
bill_to_submit['status'] = '제출됨'
congress['pending_bills'].append(bill_to_submit)
log.append(f" - {get_text_for_sim('checks_balances_bill_submitted').format(bill_name=bill_to_submit['name'])}")
game_state['pending_legislation'] = []
processed_bills_indices = []
bills_passed_this_year = 0
bills_rejected_this_year = 0
for i, bill in enumerate(congress.get('pending_bills', [])): # pending_bills 없을 경우 대비
if bill.get('status') == '제출됨':
base_chance = 0.4
relation_bonus = (congress['relations_score'] - 50) * 0.007
approval_bonus = (presidency_status['approval_rating'] - 50) * 0.003
is_minority_gov = congress['seats']['Government'] < (sum(congress['seats'].values()) / 2)
minority_penalty = -0.25 if is_minority_gov else 0
difficulty_modifier = settings['checks_balances_leniency']
pass_chance = base_chance + relation_bonus + approval_bonus + minority_penalty + difficulty_modifier
pass_chance = max(0.05, min(0.95, pass_chance))
log.append(f" - {get_text_for_sim('log_bill_pass_chance').format(bill_name=bill['name'])}: {pass_chance:.1%} (관계:{relation_bonus:.1%}, 지지도:{approval_bonus:.1%}, 여소야대:{minority_penalty:.1%}, 난이도:{difficulty_modifier:.1%})")
if random.random() < pass_chance:
bill['status'] = '통과'
log.append(f" - {get_text_for_sim('checks_balances_bill_passed').format(bill_name=bill['name'])}")
bills_passed_this_year += 1
approval_gain = math.floor(random.randint(2, 5) * (1 + settings['checks_balances_leniency']))
relation_gain = math.floor(random.randint(3, 7) * (1 + settings['checks_balances_leniency']))
presidency_status['approval_rating'] += approval_gain
congress['relations_score'] = min(100, congress['relations_score'] + relation_gain)
if approval_gain > 0: log.append(f" - {get_text_for_sim('term_approval')} {approval_gain:+}%p 상승.")
if relation_gain > 0: log.append(f" - {get_text_for_sim('term_congress_relations')} {relation_gain:+}%p 상승.")
if bill.get('goal_related') and bill['goal_related'] in presidency_status.get('policy_goals', {}): # policy_goals 없을 경우 대비
goal_to_update = bill['goal_related']
progress = random.randint(10, 25)
adjusted_progress = math.floor(progress * (1 + settings['checks_balances_leniency'] * 0.5))
current_prog = presidency_status['policy_goals'][goal_to_update]
new_prog = min(100, current_prog + adjusted_progress)
presidency_status['policy_goals'][goal_to_update] = new_prog
if new_prog > current_prog: log.append(f" - {get_text_for_sim('term_policy_goals')} '{goal_to_update}' {adjusted_progress}%p 상승 (현재 {new_prog}%)")
else:
bill['status'] = '부결'
log.append(f" - {get_text_for_sim('checks_balances_bill_failed').format(bill_name=bill['name'])}")
bills_rejected_this_year += 1
approval_loss = math.ceil(random.randint(2, 4) * (1 - settings['checks_balances_leniency']))
relation_loss = math.ceil(random.randint(2, 5) * (1 - settings['checks_balances_leniency']))
presidency_status['approval_rating'] -= approval_loss
congress['relations_score'] = max(0, congress['relations_score'] - relation_loss)
if approval_loss > 0: log.append(f" - {get_text_for_sim('term_approval')} {-approval_loss}%p 하락.")
if relation_loss > 0: log.append(f" - {get_text_for_sim('term_congress_relations')} {-relation_loss}%p 하락.")
if bill.get('goal_related') and bill['goal_related'] in presidency_status.get('policy_goals', {}) and random.random() < 0.2:
goal_to_update = bill['goal_related']
setback = random.randint(1, 5)
current_prog = presidency_status['policy_goals'][goal_to_update]
new_prog = max(0, current_prog - setback)
presidency_status['policy_goals'][goal_to_update] = new_prog
if new_prog < current_prog: log.append(f" - {get_text_for_sim('term_policy_goals')} '{goal_to_update}' {-setback}%p 후퇴 (현재 {new_prog}%)")
processed_bills_indices.append(i)
investigation_chance = 0.10
if congress['relations_score'] < 35: investigation_chance += 0.15
if difficulty == "어려움": investigation_chance += 0.10
if random.random() < investigation_chance:
log.append(f" - {get_text_for_sim('checks_balances_investigation')}")
stability_loss = math.ceil(random.randint(5, 10) * (1 - settings['checks_balances_leniency']))
relation_loss = math.ceil(random.randint(7, 14) * (1 - settings['checks_balances_leniency']))
presidency_status['stability'] -= stability_loss
congress['relations_score'] = max(0, congress['relations_score'] - relation_loss)
log.append(f" - {get_text_for_sim('term_stability')} {-stability_loss}%p 하락, {get_text_for_sim('term_congress_relations')} {-relation_loss}%p 하락.")
# 2. 사법부 시뮬레이션
ruled_cases_indices = []
if random.random() < (0.1 + (0.1 if difficulty == '어려움' else 0)):
new_case_name = f"{year}년-{random.choice(['정책','인사','법률'])}-관련소송"
judiciary['pending_cases'] = judiciary.get('pending_cases', []) # 없을 경우 초기화
judiciary['pending_cases'].append({"name": new_case_name, "status": "접수됨"})
log.append(f" - {get_text_for_sim('log_judiciary_case_received').format(judiciary_name=judiciary['name'], case_name=new_case_name)}")
judiciary['status'] = "주요 사건 심리 중"
for i, case in enumerate(judiciary.get('pending_cases', [])): # pending_cases 없을 경우 대비
if case.get('status') == '접수됨' or case.get('status') == '심리중':
independence_factor = (judiciary.get('independence_level', 80) - 75) * 0.008 # 없을 경우 기본값
difficulty_factor = settings['checks_balances_leniency'] * -1
strike_down_chance = 0.3 + independence_factor + difficulty_factor
strike_down_chance = max(0.05, min(0.95, strike_down_chance))
log.append(f" - {get_text_for_sim('log_judiciary_strike_down_chance').format(case_name=case['name'])}: {strike_down_chance:.1%} (독립성:{independence_factor:.1%}, 난이도:{difficulty_factor:.1%})")
if random.random() < strike_down_chance:
result_text = get_text_for_sim('checks_balances_judiciary_strike_down')
case['status'] = f'판결완료: {result_text}'
log.append(f" - {get_text_for_sim('checks_balances_judiciary_ruling').format(case_name=case['name'], result=result_text)}")
approval_loss = math.ceil(random.randint(5, 10) * (1 - settings['checks_balances_leniency']))
stability_loss = math.ceil(random.randint(4, 8) * (1 - settings['checks_balances_leniency']))
presidency_status['approval_rating'] -= approval_loss
presidency_status['stability'] -= stability_loss
log.append(f" - {get_text_for_sim('term_approval')} {-approval_loss}%p 하락, {get_text_for_sim('term_stability')} {-stability_loss}%p 하락.")
else:
result_text = get_text_for_sim('checks_balances_judiciary_uphold')
case['status'] = f'판결완료: {result_text}'
log.append(f" - {get_text_for_sim('checks_balances_judiciary_ruling').format(case_name=case['name'], result=result_text)}")
approval_gain = math.floor(random.randint(0, 2) * (1 + settings['checks_balances_leniency']))
stability_gain = math.floor(random.randint(0, 3) * (1 + settings['checks_balances_leniency']))
presidency_status['approval_rating'] += approval_gain
presidency_status['stability'] += stability_gain
if approval_gain > 0: log.append(f" - {get_text_for_sim('term_approval')} {approval_gain:+}%p 상승.")
if stability_gain > 0: log.append(f" - {get_text_for_sim('term_stability')} {stability_gain:+}%p 상승.")
ruled_cases_indices.append(i)
has_pending = any(c.get('status') == '접수됨' or c.get('status') == '심리중' for c in judiciary.get('pending_cases', []))
if not has_pending: judiciary['status'] = "특별한 사건 없음"
# 3. 연말 최종 상태 업데이트
approval_fluctuation = random.randint(-4, 4) if difficulty == "어려움" else (random.randint(-2, 5) if difficulty == "쉬움" else random.randint(-3, 3))
stability_fluctuation = random.randint(-5, 2) if difficulty == "어려움" else (random.randint(-2, 4) if difficulty == "쉬움" else random.randint(-4, 2))
presidency_status['approval_rating'] += approval_fluctuation
presidency_status['stability'] += stability_fluctuation
log.append(f" - {get_text_for_sim('log_natural_fluctuation')}: {get_text_for_sim('term_approval')} {approval_fluctuation:+}%p, {get_text_for_sim('term_stability')} {stability_fluctuation:+}%p")
presidency_status['approval_rating'] = max(0, min(100, presidency_status['approval_rating']))
presidency_status['stability'] = max(0, min(100, presidency_status['stability']))
congress['relations_score'] = max(0, min(100, congress['relations_score']))
log.append(f"--- {get_text_for_sim('checks_balances_summary').format(year=year, passed=bills_passed_this_year, failed=bills_rejected_this_year)} ---")
# --- UI 표시 함수들 ---
def display_presidency_dashboard(game_state, difficulty):
vocab_level = DIFFICULTY_LEVELS[difficulty]["vocab_level"]
get_text_for_ui = lambda key: get_text(key, vocab_level) # UI용 get_text
status = game_state['presidency_status']
st.metric(get_text_for_ui("term_approval"), f"{status['approval_rating']}%")
st.metric(get_text_for_ui("term_political_capital"), f"{status['political_capital']} P")
st.metric(get_text_for_ui("term_stability"), f"{status['stability']} / 100")
st.metric(get_text_for_ui("term_congress_relations"), f"{game_state['other_branches']['Congress']['relations_score']} / 100")
st.subheader(get_text_for_ui("term_policy_goals"))
goals = status.get('policy_goals', {}) # 없을 경우 대비
if not goals:
st.caption(get_text_for_ui("caption_no_goals"))
return
cols = st.columns(len(goals))
i = 0
for goal, progress in goals.items():
with cols[i]:
st.progress(progress / 100)
st.caption(f"{goal} ({progress}%)")
i += 1
def display_other_branches_status(game_state, difficulty):
vocab_level = DIFFICULTY_LEVELS[difficulty]["vocab_level"]
get_text_for_ui = lambda key: get_text(key, vocab_level)
congress = game_state['other_branches']['Congress']
judiciary = game_state['other_branches']['Judiciary']
with st.expander(f"{congress['name']} ({get_text_for_ui('term_congress_relations')}: {congress.get('relations_score', 50)})", expanded=True): # 기본값 추가
seats = congress.get('seats', {'Government': 150, 'Opposition': 130, 'Others': 20}) # 기본값 추가
st.markdown(f"**의석:** 여당 {seats['Government']} / 야당 {seats['Opposition']} / 기타 {seats['Others']} (총 {sum(seats.values())})")
if seats['Government'] < sum(seats.values()) / 2:
st.markdown("<span style='color:orange; font-weight:bold;'>[주의] 여소야대 상황입니다. 법안 통과가 어려울 수 있습니다.</span>", unsafe_allow_html=True)
st.markdown(f"**주요 관심사:** {', '.join(congress.get('key_focus', ['민생 안정']))}") # 기본값 추가
st.markdown(f"**현재 태도:** {congress.get('stance', '중립')}") # 기본값 추가
pending_bills_str = "\n".join([f"- {b['name']} ({b.get('status', '알수없음')})" for b in congress.get('pending_bills', []) if b.get('status') != '통과' and b.get('status') != '부결'])
if not pending_bills_str: pending_bills_str = get_text_for_ui("caption_no_pending_bills")
st.text_area(get_text_for_ui("label_pending_bills"), pending_bills_str, height=100, disabled=True, key="pending_bills_display")
with st.expander(f"{judiciary['name']} (독립성: {judiciary.get('independence_level', 80)})", expanded=True): # 기본값 추가
st.markdown(f"**현재 상태:** {judiciary.get('status', '사건 없음')}") # 기본값 추가
pending_cases_str = "\n".join([f"- {c['name']} ({c.get('status', '심리중')})" for c in judiciary.get('pending_cases', []) if '판결완료' not in c.get('status', '')])
if not pending_cases_str: pending_cases_str = get_text_for_ui("caption_no_pending_cases")
st.text_area(get_text_for_ui("label_pending_cases"), pending_cases_str, height=100, disabled=True, key="pending_cases_display")
def display_cabinet_status(game_state, difficulty):
vocab_level = DIFFICULTY_LEVELS[difficulty]["vocab_level"]
get_text_for_ui = lambda key: get_text(key, vocab_level)
cabinet = game_state.get('cabinet', {})
with st.expander(get_text_for_ui("cabinet_title")):
if not cabinet:
st.caption(get_text_for_ui("caption_no_cabinet"))
return
cols = st.columns(len(cabinet))
i = 0
for minister, data in cabinet.items():
with cols[i]:
st.metric(f"{minister} 장관 {get_text_for_ui('term_approval')}", f"{data.get('minister_approval', 50)}%") # 기본값 추가
i += 1
def display_gov_terms_glossary(difficulty):
vocab_level = DIFFICULTY_LEVELS[difficulty]["vocab_level"]
get_text_for_ui = lambda key: get_text(key, vocab_level)
glossary_keys = [k for k in ALL_TEXTS if k.startswith('glossary_')]
with st.sidebar.expander(get_text_for_ui("sidebar_glossary_title"), expanded=False):
for key in glossary_keys:
term_definition = get_text_for_ui(key)
parts = term_definition.split(':', 1)
term = parts[0].strip()
definition = parts[1].strip() if len(parts) > 1 else ""
st.markdown(f"**{term}:** {definition}")
st.markdown("---")
# --- 메인 게임 루프 ---
def main():
# 페이지 설정은 스크립트 최상단으로 이동했으므로 여기서는 호출하지 않음
if not st.session_state.game_started:
# 시작 화면 처리 루틴이 이 함수 위에 있으므로,
# game_started가 False이면 이 함수는 호출되지 않거나,
# 호출되더라도 바로 종료되어야 함.
# 만약을 위해 return 추가
return
# 게임 진행 중 사용할 난이도 및 어휘 수준 로드
# st.session_state에 값이 없을 경우를 대비해 기본값 설정
difficulty = st.session_state.get('difficulty', '보통')
settings = DIFFICULTY_LEVELS[difficulty]
vocab_level = settings["vocab_level"]
get_text_main = lambda key: get_text(key, vocab_level) # main 함수 내에서 사용할 get_text
st.title("👑 대통령의 균형추: 삼권분립 리더십 시뮬레이션")
st.caption(f"난이도: {difficulty}")
# game_state가 없을 경우 초기화 (오류 방지)
if 'game_state' not in st.session_state:
st.session_state.game_state = initialize_government(difficulty)
st.warning("게임 상태가 초기화되었습니다.") # 사용자에게 알림
st.rerun() # 초기화 후 다시 실행
game_state = st.session_state.game_state
# --- 게임 오버 처리 ---
if st.session_state.get('game_over', False):
st.balloons()
st.header(get_text_main("game_over_title").format(years=MAX_YEARS))
st.subheader(get_text_main("game_over_subtitle"))
final_status = game_state.get('presidency_status', {}) # 없을 경우 대비
other_branches = game_state.get('other_branches', {})
congress_state = other_branches.get('Congress', {})
col1, col2, col3 = st.columns(3)
with col1: st.metric(get_text_main("term_approval"), f"{final_status.get('approval_rating', 'N/A')}%")
with col2: st.metric(get_text_main("term_stability"), f"{final_status.get('stability', 'N/A')}")
with col3: st.metric(get_text_main("term_congress_relations"), f"{congress_state.get('relations_score', 'N/A')}")
st.write(get_text_main("term_policy_goals"))
goals = final_status.get('policy_goals', {})
if goals:
for goal, progress in goals.items():
st.progress(progress / 100, text=f"{goal} ({progress}%)")
else:
st.caption(get_text_main("caption_no_goals"))
st.subheader(get_text_main("term_event_log"))
log_text = "\n".join(game_state.get('event_log', [])[::-1]) # 없을 경우 대비
st.text_area(get_text_main("term_event_log"), log_text, height=400, disabled=True, key="final_log")
if st.button(get_text_main("button_restart")):
keys_to_delete = [k for k in st.session_state.keys() if k != 'game_started']
for key in keys_to_delete: del st.session_state[key]
st.session_state.game_started = False
st.rerun()
return
# --- 메인 게임 화면 ---
col_dashboard, col_agenda, col_actions = st.columns([1.2, 1.8, 1.5])
with col_dashboard:
st.header(get_text_main("dashboard_title"))
st.markdown(f"**{get_text_main('dashboard_term').format(year=st.session_state.get('game_year', 1), quarter=st.session_state.get('game_quarter', 1))}**") # 기본값 추가
display_presidency_dashboard(game_state, difficulty)
st.divider()
st.subheader(get_text_main("institutions_title"))
display_other_branches_status(game_state, difficulty)
st.divider()
display_cabinet_status(game_state, difficulty)
with col_agenda:
st.header(get_text_main("agenda_title"))
if 'current_quarterly_event' not in st.session_state or st.session_state['current_quarterly_event'] is None:
spinner_text = get_text_main('status_loading_report').format(year=st.session_state.get('game_year', 1), quarter=st.session_state.get('game_quarter', 1))
with st.spinner(spinner_text):
time.sleep(0.5)
event = generate_quarterly_event(st.session_state.get('game_year', 1), st.session_state.get('game_quarter', 1), game_state, difficulty)
if event:
st.session_state['current_quarterly_event'] = event
st.session_state['event_briefing'] = provide_event_briefing(event, game_state, difficulty)
st.session_state['actions_taken_this_quarter'] = 0
st.rerun()
else:
st.error(get_text_main("error_event_generation_failed"))
if st.session_state.get('current_quarterly_event'): # get 사용
event_data = st.session_state['current_quarterly_event']
title_prefix = get_text_main('agenda_event_title').split(':')[0]
st.subheader(f"{title_prefix}: {event_data.get('title', '알 수 없는 현안')}") # get 사용
st.markdown(event_data.get('description', '')) # get 사용
st.divider()
if st.session_state.get('event_briefing'): # get 사용
st.subheader(get_text_main("briefing_title"))
st.markdown(st.session_state['event_briefing'].get('text', ''), unsafe_allow_html=True) # get 사용
else:
st.warning(get_text_main("warning_briefing_loading"))
elif 'game_started' in st.session_state and st.session_state.game_started: # 게임 시작 후에만 표시
st.info(get_text_main("status_waiting_report"))
with col_actions:
st.header(get_text_main("office_title"))
st.markdown(get_text_main("office_available_capital").format(capital=game_state.get('presidency_status', {}).get('political_capital', 0))) # 기본값 추가
remaining_actions = QUARTERLY_ACTIONS_LIMIT - st.session_state.get('actions_taken_this_quarter', 0) # 기본값 추가
st.warning(get_text_main("office_action_limit").format(remaining=remaining_actions))
action_possible = (st.session_state.get('current_quarterly_event') and
st.session_state.get('event_briefing') and
remaining_actions > 0)
if action_possible:
st.subheader(get_text_main("office_issue_actions"))
options = st.session_state.get('event_briefing', {}).get("options", []) # 기본값 추가
for i, opt in enumerate(options):
button_label = get_text_main('action_button_label').format(action=opt.get('action', '알 수 없는 행동'), cost=opt.get('cost', 0)) # 기본값 추가
button_key = f"action_{st.session_state.get('game_year', 1)}_{st.session_state.get('game_quarter', 1)}_{i}"
is_disabled = (game_state.get('presidency_status', {}).get('political_capital', 0) < opt.get('cost', 0))
if st.button(button_label, key=button_key, use_container_width=True, disabled=is_disabled):
if execute_presidential_action(i, st.session_state['event_briefing'], game_state, difficulty):
st.rerun()
if remaining_actions > 0:
st.divider()
st.subheader(get_text_main("office_standard_actions"))
# 상시 행동 정의 시 cabinet 존재 여부 확인
cabinet_ministers = list(game_state.get('cabinet', {}).keys())
default_minister = "특정" if not cabinet_ministers else random.choice(cabinet_ministers)
standard_actions_defs = [
{"action_key": "action_replace_minister", "cost_base": 4, "context": {"minister": default_minister}},
{"action_key": "action_prepare_bill", "cost_base": 3},
{"action_key": "action_meet_ruling_party", "cost_base": 2, "impact": {"congress_relations": random.randint(1,4)}},
{"action_key": "action_meet_opposition", "cost_base": 3},
{"action_key": "action_meet_biz_leaders", "cost_base": 1, "impact": {"approval": random.randint(0,2)}},
{"action_key": "action_respect_judiciary", "cost_base": 1},
]
for j, std_def in enumerate(standard_actions_defs):
cost = math.ceil(std_def['cost_base'] * (1/settings['event_impact_modifier']))
action_text = get_text_main(std_def['action_key']).format(**std_def.get("context", {}))
button_label = get_text_main('action_button_label').format(action=action_text, cost=cost)
button_key = f"std_action_{st.session_state.get('game_year', 1)}_{st.session_state.get('game_quarter', 1)}_{j}"
is_disabled = (game_state.get('presidency_status', {}).get('political_capital', 0) < cost)
temp_briefing_data = {"options": [{
"action": action_text,
"cost": cost,
"impact": std_def.get("impact", {})
}]}
if st.button(button_label, key=button_key, use_container_width=True, disabled=is_disabled):
if execute_presidential_action(0, temp_briefing_data, game_state, difficulty):
st.rerun()
elif st.session_state.get('actions_taken_this_quarter', 0) >= QUARTERLY_ACTIONS_LIMIT and st.session_state.get('current_quarterly_event'):
st.info(get_text_main("status_action_limit_reached").format(quarter=st.session_state.get('game_quarter', 1)))
elif 'game_started' in st.session_state and st.session_state.game_started and not st.session_state.get('current_quarterly_event'): # 게임 시작 후 & 이벤트 없을 때
st.info(get_text_main("status_waiting_report"))
st.divider()
if st.button(get_text_main("button_next_quarter"), use_container_width=True, key="next_quarter_button"):
current_quarter = st.session_state.get('game_quarter', 1)
current_year = st.session_state.get('game_year', 1)
if current_quarter == 4:
spinner_text = f"{current_year}년차 국정 결산 중..." # 키로 관리 가능
with st.spinner(spinner_text):
simulate_checks_and_balances(game_state, difficulty)
capital_gain = settings['yearly_gain']
game_state['presidency_status']['political_capital'] = game_state.get('presidency_status', {}).get('political_capital', 0) + capital_gain # 안전하게 접근
game_state['event_log'] = game_state.get('event_log', []) # 로그 없을 경우 초기화
game_state['event_log'].append(f" - {get_text_main('log_yearly_capital_gain')} {capital_gain}P 획득.")
time.sleep(1.5)
st.session_state['game_year'] = current_year + 1
st.session_state['game_quarter'] = 1
if st.session_state['game_year'] > MAX_YEARS:
st.session_state['game_over'] = True
game_state['event_log'].append(get_text_main('log_end_of_term'))
else:
game_state['event_log'].append(get_text_main('log_start_year').format(year=st.session_state['game_year']))
else:
st.session_state['game_quarter'] = current_quarter + 1
game_state['event_log'] = game_state.get('event_log', [])
game_state['event_log'].append(get_text_main('log_start_quarter').format(year=current_year, quarter=st.session_state['game_quarter']))
st.session_state['current_quarterly_event'] = None
st.session_state['event_briefing'] = None
st.session_state['actions_taken_this_quarter'] = 0
st.rerun()
st.divider()
st.subheader(get_text_main("term_event_log"))
log_text = "\n".join(game_state.get('event_log', [])[::-1])
st.text_area(get_text_main("term_event_log"), log_text, height=300, disabled=True, key="event_log_area_main")
with st.sidebar:
st.header(get_text_main("sidebar_title"))
display_gov_terms_glossary(difficulty)
st.divider()
st.markdown("---")
st.caption(f"현재 난이도: {difficulty}")
if st.button(get_text_main("button_restart") + " (난이도 변경)"):
keys_to_delete = [k for k in st.session_state.keys() if k != 'game_started']
for key in keys_to_delete: del st.session_state[key]
st.session_state.game_started = False
st.rerun()
if __name__ == "__main__":
main() |