Spaces:
Build error
Build error
| import streamlit as st | |
| import numpy as np | |
| from objects import Player, HumanPlayer, State | |
| import random | |
| # Create RL bot players | |
| p1 = Player("p1") | |
| p2 = HumanPlayer("p2") | |
| # Initialize the selected opponent in Session State | |
| if "selected_opponent" not in st.session_state: | |
| st.session_state.selected_opponent = 'Computer' | |
| def handle_click(i, j): | |
| if (i, j) not in check_available_moves(extra=True): | |
| st.session_state.warning = True | |
| elif not st.session_state.winner: | |
| st.session_state.warning = False | |
| st.session_state.board[i, j] = st.session_state.player | |
| st.session_state.player = "O" if st.session_state.player == "X" else "X" | |
| winner = check_win(st.session_state.board) | |
| if winner != ".": | |
| st.session_state.winner = winner | |
| if st.session_state.opponent == 'Computer': | |
| # Give reward to the RL bot and update its policy | |
| if winner == 'X': | |
| p1.feedReward(1) | |
| elif winner == 'O': | |
| p1.feedReward(0) | |
| else: | |
| p1.feedReward(0.1) | |
| # Save the RL bot's policy | |
| p1.savePolicy() | |
| def init(post_init=False): | |
| if not post_init: | |
| st.session_state.opponent = 'Human' | |
| st.session_state.win = {'X': 0, 'O': 0} | |
| st.session_state.board = np.full((3, 3), '.', dtype=str) | |
| st.session_state.player = 'X' | |
| st.session_state.warning = False | |
| st.session_state.winner = None | |
| st.session_state.over = False | |
| def check_available_moves(extra=False) -> list: | |
| raw_moves = [row for col in st.session_state.board.tolist() for row in col] | |
| num_moves = [i for i, spot in enumerate(raw_moves) if spot == '.'] | |
| if extra: | |
| return [(i // 3, i % 3) for i in num_moves] | |
| return num_moves | |
| def check_rows(board): | |
| for row in board: | |
| if len(set(row)) == 1: | |
| return row[0] | |
| return None | |
| def check_diagonals(board): | |
| if len(set([board[i][i] for i in range(len(board))])) == 1: | |
| return board[0][0] | |
| if len(set([board[i][len(board) - i - 1] for i in range(len(board))])) == 1: | |
| return board[0][len(board) - 1] | |
| return None | |
| def check_state(): | |
| if st.session_state.winner: | |
| st.success(f"Congrats! {st.session_state.winner} won the game! π") | |
| if st.session_state.warning and not st.session_state.over: | |
| st.warning('β οΈ This move already exists') | |
| if st.session_state.winner and not st.session_state.over: | |
| st.session_state.over = True | |
| st.session_state.win[st.session_state.winner] = ( | |
| st.session_state.win.get(st.session_state.winner, 0) + 1 | |
| ) | |
| elif not check_available_moves() and not st.session_state.winner: | |
| st.info(f"It's a tie π") | |
| st.session_state.over = True | |
| def check_win(board): | |
| for new_board in [board, np.transpose(board)]: | |
| result = check_rows(new_board) | |
| if result: | |
| return result | |
| return check_diagonals(board) | |
| def computer_player(): | |
| moves = check_available_moves(extra=True) | |
| if moves: | |
| # Use p1 to choose the action | |
| positions = check_available_moves(extra=True) | |
| p1_action = p1.chooseAction(positions, st.session_state.board, -1) | |
| # Check if the chosen action is valid and make the move | |
| if p1_action in moves: | |
| i, j = p1_action | |
| handle_click(i, j) | |
| def on_opponent_selected(): | |
| st.session_state.selected_opponent = st.session_state.opponent | |
| init(True) | |
| def main(): | |
| st.write( | |
| """ | |
| # βπ ΎοΈ Tic Tac Toe | |
| """ | |
| ) | |
| if "board" not in st.session_state: | |
| init() | |
| reset, score, player, settings = st.columns([0.5, 0.6, 1, 1]) | |
| reset.button('New game', on_click=init, args=(True,)) | |
| # Dynamically set the Expander label based on the selected opponent | |
| with settings.expander(f'Opponent ({st.session_state.selected_opponent})'): | |
| st.write('**Warning**: changing this setting will restart your game') | |
| st.selectbox( | |
| 'Set opponent', | |
| ['Computer','Human'], | |
| key='opponent', | |
| on_change=on_opponent_selected, | |
| ) | |
| for i, row in enumerate(st.session_state.board): | |
| cols = st.columns([5, 1, 1, 1, 5]) | |
| for j, field in enumerate(row): | |
| cols[j + 1].button( | |
| field, | |
| key=f"{i}-{j}", | |
| on_click=handle_click | |
| if st.session_state.player == 'X' | |
| or st.session_state.opponent == 'Human' | |
| else computer_player(), | |
| args=(i, j), | |
| ) | |
| check_state() | |
| score.button(f'β{st.session_state.win["X"]} π {st.session_state.win["O"]}β') | |
| player.button( | |
| f'{"β" if st.session_state.player == "X" else "β"}\'s turn' | |
| if not st.session_state.winner | |
| else f'π Game finished' | |
| ) | |
| if __name__ == '__main__': | |
| main() | |