Tic-Tac-Toe-RL / app.py
TuanScientist's picture
Update app.py
3f02d24
raw
history blame contribute delete
No virus
5 kB
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()