final changes
This commit is contained in:
parent
1df6d6d62f
commit
d47f8d479d
3 changed files with 73 additions and 76 deletions
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
This is a simple Chess game that uses an AI agents - Player black and player white to play the game. There's also a board proxy agent to execute the tools and manage the game. It is important to use a board proxy as a non-LLM "guard rail" to ensure the game is played correctly and to prevent agents from making illegal moves.
|
This is a simple Chess game that uses an AI agents - Player black and player white to play the game. There's also a board proxy agent to execute the tools and manage the game. It is important to use a board proxy as a non-LLM "guard rail" to ensure the game is played correctly and to prevent agents from making illegal moves.
|
||||||
|
|
||||||
Two agents (player_white and player_black) are initialized using the OpenAI API key. These agents are configured to play chess as white and black, respectively.
|
Two agents (agent_white and agent_black) are initialized using the OpenAI API key. These agents are configured to play chess as white and black, respectively.
|
||||||
A board_proxy agent is created to manage the board state and validate moves.
|
A board_proxy agent is created to manage the board state and validate moves.
|
||||||
Functions (make_move and available_moves) are registered with the agents to allow them to interact with the board.
|
Functions (make_move and available_moves) are registered with the agents to allow them to interact with the board.
|
||||||
|
|
||||||
|
|
@ -29,10 +29,3 @@ pip install -r requirements.txt
|
||||||
streamlit run ai_chess_agents.py
|
streamlit run ai_chess_agents.py
|
||||||
```
|
```
|
||||||
|
|
||||||
## Requirements
|
|
||||||
|
|
||||||
- autogen
|
|
||||||
- numpy
|
|
||||||
- openai
|
|
||||||
- streamlit
|
|
||||||
- chess
|
|
||||||
|
|
@ -3,7 +3,6 @@ import chess.svg
|
||||||
import streamlit as st
|
import streamlit as st
|
||||||
from autogen import ConversableAgent, register_function
|
from autogen import ConversableAgent, register_function
|
||||||
|
|
||||||
# Initialize session state for the OpenAI API key and game state
|
|
||||||
if "openai_api_key" not in st.session_state:
|
if "openai_api_key" not in st.session_state:
|
||||||
st.session_state.openai_api_key = None
|
st.session_state.openai_api_key = None
|
||||||
if "board" not in st.session_state:
|
if "board" not in st.session_state:
|
||||||
|
|
@ -14,49 +13,64 @@ if "board_svg" not in st.session_state:
|
||||||
st.session_state.board_svg = None
|
st.session_state.board_svg = None
|
||||||
if "move_history" not in st.session_state:
|
if "move_history" not in st.session_state:
|
||||||
st.session_state.move_history = []
|
st.session_state.move_history = []
|
||||||
|
if "max_turns" not in st.session_state:
|
||||||
|
st.session_state.max_turns = 5
|
||||||
|
|
||||||
# Streamlit sidebar for OpenAI API key input
|
|
||||||
st.sidebar.title("Chess Agent Configuration")
|
st.sidebar.title("Chess Agent Configuration")
|
||||||
openai_api_key = st.sidebar.text_input("Enter your OpenAI API key:", type="password")
|
openai_api_key = st.sidebar.text_input("Enter your OpenAI API key:", type="password")
|
||||||
if openai_api_key:
|
if openai_api_key:
|
||||||
st.session_state.openai_api_key = openai_api_key
|
st.session_state.openai_api_key = openai_api_key
|
||||||
st.sidebar.success("API key saved!")
|
st.sidebar.success("API key saved!")
|
||||||
|
|
||||||
# Function to get legal moves
|
st.sidebar.info("""
|
||||||
|
For a complete chess game with potential checkmate, it would take max_turns > 200 approximately.
|
||||||
|
However, this will consume significant API credits and a lot of time.
|
||||||
|
For demo purposes, using 5-10 turns is recommended.
|
||||||
|
""")
|
||||||
|
|
||||||
|
max_turns_input = st.sidebar.number_input(
|
||||||
|
"Enter the number of turns (max_turns):",
|
||||||
|
min_value=1,
|
||||||
|
max_value=1000,
|
||||||
|
value=st.session_state.max_turns,
|
||||||
|
step=1
|
||||||
|
)
|
||||||
|
|
||||||
|
if max_turns_input:
|
||||||
|
st.session_state.max_turns = max_turns_input
|
||||||
|
st.sidebar.success(f"Max turns of total chess moves set to {st.session_state.max_turns}!")
|
||||||
|
|
||||||
|
st.title("Chess with AutoGen Agents")
|
||||||
|
|
||||||
def available_moves() -> str:
|
def available_moves() -> str:
|
||||||
available_moves = [str(move) for move in st.session_state.board.legal_moves]
|
available_moves = [str(move) for move in st.session_state.board.legal_moves]
|
||||||
return "Available moves are: " + ",".join(available_moves)
|
return "Available moves are: " + ",".join(available_moves)
|
||||||
|
|
||||||
# Function to make a move
|
def execute_move(move: str) -> str:
|
||||||
def make_move(move: str) -> str:
|
|
||||||
try:
|
try:
|
||||||
chess_move = chess.Move.from_uci(move)
|
chess_move = chess.Move.from_uci(move)
|
||||||
if chess_move not in st.session_state.board.legal_moves:
|
if chess_move not in st.session_state.board.legal_moves:
|
||||||
return f"Invalid move: {move}. Please call get_legal_moves() to see valid moves."
|
return f"Invalid move: {move}. Please call available_moves() to see valid moves."
|
||||||
|
|
||||||
st.session_state.board.push(chess_move)
|
st.session_state.board.push(chess_move)
|
||||||
st.session_state.made_move = True
|
st.session_state.made_move = True
|
||||||
|
|
||||||
# Render board visualization
|
|
||||||
board_svg = chess.svg.board(
|
board_svg = chess.svg.board(
|
||||||
st.session_state.board,
|
st.session_state.board,
|
||||||
arrows=[(chess_move.from_square, chess_move.to_square)],
|
arrows=[(chess_move.from_square, chess_move.to_square)],
|
||||||
fill={chess_move.from_square: "gray"},
|
fill={chess_move.from_square: "gray"},
|
||||||
size=400
|
size=400
|
||||||
)
|
)
|
||||||
st.session_state.board_svg = board_svg # Save SVG to session state
|
st.session_state.board_svg = board_svg
|
||||||
st.session_state.move_history.append(board_svg) # Save move history
|
st.session_state.move_history.append(board_svg)
|
||||||
|
|
||||||
# Get moved piece details
|
|
||||||
moved_piece = st.session_state.board.piece_at(chess_move.to_square)
|
moved_piece = st.session_state.board.piece_at(chess_move.to_square)
|
||||||
piece_unicode = moved_piece.unicode_symbol()
|
piece_unicode = moved_piece.unicode_symbol()
|
||||||
piece_type_name = chess.piece_name(moved_piece.piece_type)
|
piece_type_name = chess.piece_name(moved_piece.piece_type)
|
||||||
piece_name = piece_type_name.capitalize() if piece_unicode.isupper() else piece_type_name
|
piece_name = piece_type_name.capitalize() if piece_unicode.isupper() else piece_type_name
|
||||||
|
|
||||||
# Build move description
|
|
||||||
move_desc = f"Moved {piece_name} ({piece_unicode}) from {chess.SQUARE_NAMES[chess_move.from_square]} to {chess.SQUARE_NAMES[chess_move.to_square]}."
|
move_desc = f"Moved {piece_name} ({piece_unicode}) from {chess.SQUARE_NAMES[chess_move.from_square]} to {chess.SQUARE_NAMES[chess_move.to_square]}."
|
||||||
|
|
||||||
# Check game state
|
|
||||||
if st.session_state.board.is_checkmate():
|
if st.session_state.board.is_checkmate():
|
||||||
winner = 'White' if st.session_state.board.turn == chess.BLACK else 'Black'
|
winner = 'White' if st.session_state.board.turn == chess.BLACK else 'Black'
|
||||||
move_desc += f"\nCheckmate! {winner} wins!"
|
move_desc += f"\nCheckmate! {winner} wins!"
|
||||||
|
|
@ -71,7 +85,6 @@ def make_move(move: str) -> str:
|
||||||
except ValueError:
|
except ValueError:
|
||||||
return f"Invalid move format: {move}. Please use UCI format (e.g., 'e2e4')."
|
return f"Invalid move format: {move}. Please use UCI format (e.g., 'e2e4')."
|
||||||
|
|
||||||
# Check if the player has made a move, and reset the flag if move is made.
|
|
||||||
def check_made_move(msg):
|
def check_made_move(msg):
|
||||||
if st.session_state.made_move:
|
if st.session_state.made_move:
|
||||||
st.session_state.made_move = False
|
st.session_state.made_move = False
|
||||||
|
|
@ -79,119 +92,112 @@ def check_made_move(msg):
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Initialize players and proxy agent if API key is provided
|
|
||||||
if st.session_state.openai_api_key:
|
if st.session_state.openai_api_key:
|
||||||
try:
|
try:
|
||||||
player_white_config_list = [
|
agent_white_config_list = [
|
||||||
{
|
{
|
||||||
"model": "gpt-4o-mini",
|
"model": "gpt-4o-mini",
|
||||||
"api_key": st.session_state.openai_api_key,
|
"api_key": st.session_state.openai_api_key,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
player_black_config_list = [
|
agent_black_config_list = [
|
||||||
{
|
{
|
||||||
"model": "gpt-4o-mini",
|
"model": "gpt-4o-mini",
|
||||||
"api_key": st.session_state.openai_api_key,
|
"api_key": st.session_state.openai_api_key,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
player_white = ConversableAgent(
|
agent_white = ConversableAgent(
|
||||||
name="Player_White",
|
name="Agent_White",
|
||||||
system_message="You are a professional chess player and you play as white. "
|
system_message="You are a professional chess player and you play as white. "
|
||||||
"First call get_legal_moves() first, to get list of legal moves. "
|
"First call available_moves() first, to get list of legal moves. "
|
||||||
"Then call make_move(move) to make a move.",
|
"Then call execute_move(move) to make a move.",
|
||||||
llm_config={"config_list": player_white_config_list, "cache_seed": None},
|
llm_config={"config_list": agent_white_config_list, "cache_seed": None},
|
||||||
)
|
)
|
||||||
|
|
||||||
player_black = ConversableAgent(
|
agent_black = ConversableAgent(
|
||||||
name="Player_Black",
|
name="Agent_Black",
|
||||||
system_message="You are a professional chess player and you play as black. "
|
system_message="You are a professional chess player and you play as black. "
|
||||||
"First call get_legal_moves() first, to get list of legal moves. "
|
"First call available_moves() first, to get list of legal moves. "
|
||||||
"Then call make_move(move) to make a move.",
|
"Then call execute_move(move) to make a move.",
|
||||||
llm_config={"config_list": player_black_config_list, "cache_seed": None},
|
llm_config={"config_list": agent_black_config_list, "cache_seed": None},
|
||||||
)
|
)
|
||||||
|
|
||||||
# Proxy agent to manage the board and validate moves
|
game_master = ConversableAgent(
|
||||||
board_proxy = ConversableAgent(
|
name="Game_Master",
|
||||||
name="Board_Proxy",
|
|
||||||
llm_config=False,
|
llm_config=False,
|
||||||
is_termination_msg=check_made_move,
|
is_termination_msg=check_made_move,
|
||||||
default_auto_reply="Please make a move.",
|
default_auto_reply="Please make a move.",
|
||||||
human_input_mode="NEVER",
|
human_input_mode="NEVER",
|
||||||
)
|
)
|
||||||
|
|
||||||
# Register functions for both players
|
|
||||||
register_function(
|
register_function(
|
||||||
make_move,
|
execute_move,
|
||||||
caller=player_white,
|
caller=agent_white,
|
||||||
executor=board_proxy,
|
executor=game_master,
|
||||||
name="make_move",
|
name="execute_move",
|
||||||
description="Call this tool to make a move.",
|
description="Call this tool to make a move.",
|
||||||
)
|
)
|
||||||
|
|
||||||
register_function(
|
register_function(
|
||||||
available_moves,
|
available_moves,
|
||||||
caller=player_white,
|
caller=agent_white,
|
||||||
executor=board_proxy,
|
executor=game_master,
|
||||||
name="available_moves",
|
name="available_moves",
|
||||||
description="Get legal moves.",
|
description="Get legal moves.",
|
||||||
)
|
)
|
||||||
|
|
||||||
register_function(
|
register_function(
|
||||||
make_move,
|
execute_move,
|
||||||
caller=player_black,
|
caller=agent_black,
|
||||||
executor=board_proxy,
|
executor=game_master,
|
||||||
name="make_move",
|
name="execute_move",
|
||||||
description="Call this tool to make a move.",
|
description="Call this tool to make a move.",
|
||||||
)
|
)
|
||||||
|
|
||||||
register_function(
|
register_function(
|
||||||
available_moves,
|
available_moves,
|
||||||
caller=player_black,
|
caller=agent_black,
|
||||||
executor=board_proxy,
|
executor=game_master,
|
||||||
name="available_moves",
|
name="available_moves",
|
||||||
description="Get legal moves.",
|
description="Get legal moves.",
|
||||||
)
|
)
|
||||||
|
|
||||||
# Register nested chats for both players
|
agent_white.register_nested_chats(
|
||||||
player_white.register_nested_chats(
|
trigger=agent_black,
|
||||||
trigger=player_black,
|
|
||||||
chat_queue=[
|
chat_queue=[
|
||||||
{
|
{
|
||||||
"sender": board_proxy,
|
"sender": game_master,
|
||||||
"recipient": player_white,
|
"recipient": agent_white,
|
||||||
"summary_method": "last_msg",
|
"summary_method": "last_msg",
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
player_black.register_nested_chats(
|
agent_black.register_nested_chats(
|
||||||
trigger=player_white,
|
trigger=agent_white,
|
||||||
chat_queue=[
|
chat_queue=[
|
||||||
{
|
{
|
||||||
"sender": board_proxy,
|
"sender": game_master,
|
||||||
"recipient": player_black,
|
"recipient": agent_black,
|
||||||
"summary_method": "last_msg",
|
"summary_method": "last_msg",
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
# Streamlit UI for playing the game
|
|
||||||
st.title("Chess with AG2 Agents")
|
|
||||||
st.info("""
|
st.info("""
|
||||||
This chess game is played between two AutoGen AI agents:
|
This chess game is played between two AG2 AI agents:
|
||||||
- **Player White**: A GPT-4o-mini powered chess player controlling white pieces
|
- **Agent White**: A GPT-4o-mini powered chess player controlling white pieces
|
||||||
- **Player Black**: A GPT-4o-mini powered chess player controlling black pieces
|
- **Agent Black**: A GPT-4o-mini powered chess player controlling black pieces
|
||||||
|
|
||||||
The game is managed by a **Board Proxy Agent** that:
|
The game is managed by a **Game Master** that:
|
||||||
- Validates all moves
|
- Validates all moves
|
||||||
- Updates the chess board
|
- Updates the chess board
|
||||||
- Manages turn-taking between players
|
- Manages turn-taking between players
|
||||||
- Provides legal move information
|
- Provides legal move information
|
||||||
""")
|
""")
|
||||||
|
|
||||||
# Display the initial board state before the game starts
|
|
||||||
initial_board_svg = chess.svg.board(st.session_state.board, size=300)
|
initial_board_svg = chess.svg.board(st.session_state.board, size=300)
|
||||||
st.subheader("Initial Board")
|
st.subheader("Initial Board")
|
||||||
st.image(initial_board_svg)
|
st.image(initial_board_svg)
|
||||||
|
|
@ -199,28 +205,26 @@ The game is managed by a **Board Proxy Agent** that:
|
||||||
if st.button("Start Game"):
|
if st.button("Start Game"):
|
||||||
st.session_state.board.reset()
|
st.session_state.board.reset()
|
||||||
st.session_state.made_move = False
|
st.session_state.made_move = False
|
||||||
st.session_state.move_history = [] # Reset move history
|
st.session_state.move_history = []
|
||||||
st.session_state.board_svg = chess.svg.board(st.session_state.board, size=300)
|
st.session_state.board_svg = chess.svg.board(st.session_state.board, size=300)
|
||||||
st.info("The AI agents will now play against each other. Each agent will analyze the board, "
|
st.info("The AI agents will now play against each other. Each agent will analyze the board, "
|
||||||
"request legal moves, and make strategic decisions.")
|
"request legal moves from the Game Master (proxy agent), and make strategic decisions.")
|
||||||
|
st.success("You can view the interaction between the agents in the terminal output, after the turns between agents end, you get view all the chess board moves displayed below!")
|
||||||
st.write("Game started! White's turn.")
|
st.write("Game started! White's turn.")
|
||||||
|
|
||||||
# Initiate the chat between Player_White and Board_Proxy
|
chat_result = agent_black.initiate_chat(
|
||||||
chat_result = player_black.initiate_chat(
|
recipient=agent_white,
|
||||||
recipient=player_white,
|
|
||||||
message="Let's play chess! You go first, its your move.",
|
message="Let's play chess! You go first, its your move.",
|
||||||
max_turns=5,
|
max_turns=st.session_state.max_turns,
|
||||||
summary_method="reflection_with_llm"
|
summary_method="reflection_with_llm"
|
||||||
)
|
)
|
||||||
st.markdown(chat_result.summary)
|
st.markdown(chat_result.summary)
|
||||||
|
|
||||||
# Display the move history (boards for each move)
|
|
||||||
st.subheader("Move History")
|
st.subheader("Move History")
|
||||||
for i, move_svg in enumerate(st.session_state.move_history):
|
for i, move_svg in enumerate(st.session_state.move_history):
|
||||||
st.write(f"Move {i + 1}")
|
st.write(f"Move {i + 1}")
|
||||||
st.image(move_svg)
|
st.image(move_svg)
|
||||||
|
|
||||||
# Reset Game button
|
|
||||||
if st.button("Reset Game"):
|
if st.button("Reset Game"):
|
||||||
st.session_state.board.reset()
|
st.session_state.board.reset()
|
||||||
st.session_state.made_move = False
|
st.session_state.made_move = False
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
streamlit
|
streamlit
|
||||||
python-chess
|
chess==1.11.1
|
||||||
autogen
|
autogen==0.6.1
|
||||||
cairosvg
|
cairosvg
|
||||||
pillow
|
pillow
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue