-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
TΓΉng TrαΊ§n
committed
Sep 18, 2023
1 parent
d229340
commit 47f7aeb
Showing
4 changed files
with
444 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,152 @@ | ||
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() |
Oops, something went wrong.