CoolFace
Apppublic

NorwayForest/Ladder_Game

sourceHugging Faceupdated 1y agoView on Hugging Face
0likes
streamlit_app.py372 linesDownload Raw Back to src
1import streamlit as st2import matplotlib3matplotlib.use('Agg')4import matplotlib.pyplot as plt5import random6import numpy as np7 8# 페이지 설정9st.set_page_config(10    page_title="🎮 Ladder Game", 11    page_icon="🎮",12    layout="wide"13)14 15def create_ladder_game(num_players, results_list):16    """사다리 게임 생성 및 실행"""17    try:18        # 결과 처리 - 수정된 부분19        if not results_list or len(results_list) == 0:20            results = [f"Result{i}" for i in range(1, num_players + 1)]21        else:22            results = results_list.copy()23            # 결과 개수 맞추기 - 부족한 경우 빈 문자열로 채우기24            while len(results) < num_players:25                results.append("")  # 빈 문자열로 채우기26            results = results[:num_players]27        28        # 사다리 생성29        ladder_height = 830        horizontal_lines = []31        32        # 랜덤 시드 설정 (재실행시 다른 결과)33        random.seed()34        35        for level in range(1, ladder_height):36            for pos in range(num_players - 1):37                if random.random() < 0.35:  # 35% 확률로 가로선 생성38                    # 인접한 가로선이 있는지 체크39                    adjacent = False40                    for line_level, line_pos in horizontal_lines:41                        if line_level == level and abs(line_pos - pos) <= 1:42                            adjacent = True43                            break44                    if not adjacent:45                        horizontal_lines.append((level, pos))46        47        # 각 참가자의 경로 추적 및 결과 계산48        final_results = {}49        all_paths = {}50        51        for start_pos in range(num_players):52            current_pos = start_pos53            path = [(0, current_pos)]54            55            # 레벨별로 경로 추적56            for level in range(1, ladder_height + 1):57                # 현재 레벨에서 가로선이 있는지 확인58                for line_level, line_pos in horizontal_lines:59                    if line_level == level:60                        if line_pos == current_pos and current_pos < num_players - 1:61                            # 오른쪽으로 이동62                            current_pos += 163                            break64                        elif line_pos == current_pos - 1 and current_pos > 0:65                            # 왼쪽으로 이동66                            current_pos -= 167                            break68                69                # 안전 장치: 범위 체크70                current_pos = max(0, min(num_players - 1, current_pos))71                path.append((level, current_pos))72            73            # 최종 결과 저장74            final_results[f"user{start_pos + 1}"] = results[current_pos]75            all_paths[f"user{start_pos + 1}"] = path76        77        # 시각화 (크기 60%로 축소)78        fig, ax = plt.subplots(figsize=(max(7.2, num_players * 1.2), 4.8))79        80        # 세로선 그리기81        for i in range(num_players):82            ax.plot([i, i], [ladder_height, 0], 'k-', linewidth=3)83        84        # 가로선 그리기85        for level, pos in horizontal_lines:86            y = ladder_height - level87            ax.plot([pos, pos + 1], [y, y], 'k-', linewidth=4)88        89        # 참가자 이름 표시90        for i in range(num_players):91            ax.text(i, ladder_height + 0.4, f"user{i+1}", 92                   ha='center', va='center', fontsize=12, fontweight='bold',93                   bbox=dict(boxstyle="round,pad=0.3", facecolor="lightblue", alpha=0.8))94        95        # 결과 표시 - 수정된 부분96        for i, result in enumerate(results):97            display_result = result if result.strip() else "빈칸"  # 빈 문자열이면 "빈칸" 표시98            result_color = "lightcoral" if result.strip() else "lightgray"  # 빈칸이면 회색99            ax.text(i, -0.4, display_result, 100                   ha='center', va='center', fontsize=11, fontweight='bold',101                   bbox=dict(boxstyle="round,pad=0.3", facecolor=result_color, alpha=0.8))102        103        # 각 참가자의 경로를 다른 색으로 표시104        colors = ['red', 'blue', 'green', 'orange', 'purple', 'brown', 'pink', 'gray', 'cyan', 'magenta']105        106        for idx in range(num_players):107            player = f"user{idx + 1}"108            path = all_paths[player]109            color = colors[idx % len(colors)]110            111            # 경로 그리기112            x_coords = [point[1] for point in path]113            y_coords = [ladder_height - point[0] for point in path]114            115            ax.plot(x_coords, y_coords, color=color, linewidth=3, alpha=0.7, label=player)116            117            # 시작점과 끝점 마킹118            ax.scatter(x_coords[0], y_coords[0], color=color, s=120, marker='o', alpha=0.9)119            ax.scatter(x_coords[-1], y_coords[-1], color=color, s=120, marker='s', alpha=0.9)120        121        # 그래프 설정122        ax.set_xlim(-0.7, num_players - 0.3)123        ax.set_ylim(-0.8, ladder_height + 0.8)124        ax.set_title("🎮 Ladder Game Results", fontsize=16, fontweight='bold', pad=20)125        ax.set_xticks([])126        ax.set_yticks([])127        ax.grid(True, alpha=0.3)128        129        # 범례 추가130        ax.legend(bbox_to_anchor=(1.05, 1), loc='upper left', fontsize=10)131        132        plt.tight_layout()133        134        return fig, final_results, results135        136    except Exception as e:137        st.error(f"오류 발생: {str(e)}")138        return None, {}, []139 140# 메인 앱 UI141def main():142    st.title("🎮 Interactive Ladder Game")143    st.markdown("### 참가자를 선택하고 결과를 입력한 후 사다리 게임을 즐겨보세요! 🎯")144    145    # 세션 상태 초기화146    if 'results_list' not in st.session_state:147        st.session_state.results_list = ["Winner", "Loser", "Coffee", "Cleaning"]148    149    if 'input_counter' not in st.session_state:150        st.session_state.input_counter = 0151    152    # 메인 레이아웃153    col1, col2 = st.columns([1, 2])154    155    with col1:156        st.subheader("🎛️ 게임 설정")157        158        # 참가자 수 선택 (화살표로 조정 가능)159        num_players = st.number_input(160            "참가자 수", 161            min_value=2, 162            max_value=10, 163            value=4, 164            step=1,165            help="화살표를 사용해서 참가자 수를 조정하세요"166        )167        168        st.markdown("---")169        170        # 결과 입력 부분 개선171        st.subheader("🎯 결과 입력")172        173        # 현재 결과 개수와 참가자 수 비교 표시 - 추가된 부분174        current_count = len(st.session_state.results_list)175        if current_count < num_players:176            st.info(f"📊 현재 {current_count}개 입력됨 (필요: {num_players}개)")177        elif current_count == num_players:178            st.success(f"✅ 결과 입력 완료! ({current_count}/{num_players}개)")179        else:180            st.warning(f"⚠️ 결과가 너무 많습니다! ({current_count}개, 필요: {num_players}개)")181        182        # 결과 입력창 (엔터키 감지를 위한 form 사용) - 수정된 부분183        with st.form(key=f"add_result_form_{st.session_state.input_counter}", clear_on_submit=True):184            new_result = st.text_input(185                "새 결과 입력 후 Enter 또는 추가 버튼 클릭",186                value="",187                help="결과를 입력하고 Enter를 누르거나 추가 버튼을 클릭하세요",188                placeholder="예: Winner, 1등, 커피 등...",189                disabled=(len(st.session_state.results_list) >= num_players)  # 최대 개수 도달시 비활성화190            )191            192            # Submit 버튼 - 수정된 부분193            submitted = st.form_submit_button(194                "➕ 추가", 195                use_container_width=True,196                disabled=(len(st.session_state.results_list) >= num_players)  # 최대 개수 도달시 비활성화197            )198            199            if submitted and new_result.strip():200                if len(st.session_state.results_list) >= num_players:201                    st.error(f"❌ 더 이상 추가할 수 없습니다! (최대 {num_players}개)")202                else:203                    st.session_state.results_list.append(new_result.strip())204                    st.session_state.input_counter += 1  # 폼 리셋을 위한 카운터205                    st.success(f"'{new_result.strip()}' 추가됨!")206                    st.rerun()207        208        # 최대 개수 도달시 안내 메시지 - 추가된 부분209        if len(st.session_state.results_list) >= num_players:210            st.info("💡 참가자 수만큼 결과가 입력되었습니다. 더 추가하려면 참가자 수를 늘리거나 기존 결과를 삭제하세요.")211        212        # 전체 삭제 버튼213        if st.button("🗑️ 전체 삭제", use_container_width=True):214            st.session_state.results_list = []215            st.success("모든 결과가 삭제되었습니다!")216            st.rerun()217        218        # 현재 결과 목록 표시219        st.markdown("**📋 현재 결과 목록:**")220        if st.session_state.results_list:221            for idx, result in enumerate(st.session_state.results_list):222                col_result, col_delete = st.columns([4, 1])223                with col_result:224                    st.markdown(f"{idx + 1}. {result}")225                with col_delete:226                    if st.button("❌", key=f"delete_{idx}", help=f"{result} 삭제"):227                        st.session_state.results_list.pop(idx)228                        st.success(f"'{result}' 삭제됨!")229                        st.rerun()230        else:231            st.info("결과가 없습니다. 위에 입력해주세요.")232        233        # 부족한 결과 안내 - 추가된 부분234        missing_count = num_players - len(st.session_state.results_list)235        if missing_count > 0:236            st.warning(f"⚠️ {missing_count}개의 결과가 부족합니다. 게임 실행시 빈칸으로 처리됩니다.")237        238        st.markdown("---")239        240        # 게임 실행 및 리셋 버튼241        col_run, col_reset = st.columns(2)242        with col_run:243            run_game = st.button("🎲 사다리 게임 실행!", type="primary", use_container_width=True)244        with col_reset:245            reset_game = st.button("🔄 Mix Again!", use_container_width=True)246        247        st.markdown("---")248        249        # 빠른 예시 버튼들250        st.subheader("🚀 빠른 예시")251        252        col_a, col_b = st.columns(2)253        with col_a:254            if st.button("🏆 승부 게임"):255                st.session_state.results_list = ["Winner", "Loser", "Draw", "Retry"]256                st.rerun()257        258        with col_b:259            if st.button("🥇 순위 게임"):260                st.session_state.results_list = ["1st", "2nd", "3rd", "4th", "5th"]261                st.rerun()262        263        col_c, col_d = st.columns(2)264        with col_c:265            if st.button("☕ 역할 분담"):266                st.session_state.results_list = ["Coffee", "Cleaning", "Rest", "Shopping"]267                st.rerun()268        269        with col_d:270            if st.button("🎁 선물 뽑기"):271                st.session_state.results_list = ["Gift A", "Gift B", "Gift C"]272                st.rerun()273    274    with col2:275        st.subheader("🎨 게임 결과")276        277        # 리셋 처리278        if reset_game:279            st.session_state.show_initial = True280            st.session_state.game_results = None281            st.success("게임이 리셋되었습니다!")282            st.rerun()283        284        # 게임 실행 또는 초기 표시285        if run_game or st.session_state.get('show_initial', True):286            with st.spinner("🎮 사다리 게임 생성 중..."):287                fig, final_results, results_final = create_ladder_game(num_players, st.session_state.results_list)288            289            if fig is not None:290                # 결과를 세션에 저장291                st.session_state.game_results = {292                    'fig': fig,293                    'final_results': final_results,294                    'results_final': results_final295                }296                st.session_state.show_initial = False297        298        # 저장된 게임 결과 표시299        if st.session_state.get('game_results') is not None:300            game_data = st.session_state.game_results301            302            # 그래프와 결과를 나란히 배치303            graph_col, result_col = st.columns([2, 1])304            305            with graph_col:306                # 그래프 표시 (60% 크기)307                st.pyplot(game_data['fig'], use_container_width=True)308                # matplotlib figure를 닫지 않고 재사용309            310            with result_col:311                # 최종 결과 표시 (세로 배치) - 수정된 부분312                st.markdown("### 🎊 최종 결과")313                314                # 결과를 세로로 표시315                for player, result in game_data['final_results'].items():316                    display_result = result if result.strip() else "빈칸"317                    st.metric(318                        label=player,319                        value=display_result,320                        delta=None321                    )322            323            # 상세 결과 (접기/펼치기) - 전체 너비로 - 수정된 부분324            with st.expander("📋 상세 결과 보기"):325                for player, result in game_data['final_results'].items():326                    display_result = result if result.strip() else "빈칸"327                    st.write(f"**{player}** ➡️ **{display_result}**")328        else:329            st.info("🎲 '사다리 게임 실행!' 버튼을 클릭해서 게임을 시작하세요!")330    331    # 사이드바 정보332    with st.sidebar:333        st.markdown("## 📖 사용법")334        st.markdown("""335        1. **참가자 수**: 화살표로 2-10명 조정336        2. **결과 입력**: 337           - 텍스트 입력 후 Enter 키 또는 "추가" 버튼338           - 참가자 수만큼만 입력 가능339           - 개별 삭제나 전체 삭제 가능340        3. **게임 실행**: 버튼 클릭으로 실행341        4. **Mix Again!**: 다시 한 번 실행합니다. 342        343        ### 💡 팁344        - Enter 키를 누르면 입력창이 자동으로 비워집니다345        - 결과가 참가자 수보다 적으면 빈칸으로 처리됩니다346        - 매번 실행할 때마다 다른 사다리가 생성됩니다347        - 빠른 예시 버튼으로 쉽게 시작하세요!348        """)349        350        st.markdown("---")351        st.markdown("### 🎯 사용 예시")352        st.markdown("""353        **결과 입력 방법:**354        1. "Winner" 입력 → Enter 키 또는 추가 버튼355        2. "Loser" 입력 → Enter 키 또는 추가 버튼356        3. "Coffee" 입력 → Enter 키 또는 추가 버튼357        4. "Cleaning" 입력 → Enter 키 또는 추가 버튼358        359        **참가자 수와 결과 개수가 다른 경우:**360        - 결과가 부족하면: 빈칸으로 처리361        - 결과가 많으면: 더 이상 입력 불가362        """)363        364        st.markdown("---")365        st.markdown("**🎮 Made with Streamlit**")366 367if __name__ == "__main__":368    # 세션 상태 초기화369    if 'show_initial' not in st.session_state:370        st.session_state.show_initial = True371    372    main()