You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

main.py 5.4 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. import argparse
  2. import asyncio
  3. from autogen_agentchat.messages import TextMessage
  4. import yaml
  5. import random
  6. import chess
  7. from autogen_agentchat.agents import AssistantAgent
  8. from autogen_agentchat.ui import Console
  9. from autogen_core.model_context import BufferedChatCompletionContext
  10. from autogen_core.models import ChatCompletionClient
  11. def create_ai_player(model_client: ChatCompletionClient) -> AssistantAgent:
  12. # Create an agent that can use the model client.
  13. player = AssistantAgent(
  14. name="ai_player",
  15. model_client=model_client,
  16. system_message=None,
  17. model_client_stream=True, # Enable streaming for the model client.
  18. model_context=BufferedChatCompletionContext(buffer_size=10), # Model context limited to the last 10 messages.
  19. )
  20. return player
  21. def get_random_move(board: chess.Board) -> str:
  22. legal_moves = list(board.legal_moves)
  23. move = random.choice(legal_moves)
  24. return move.uci()
  25. def get_ai_prompt(board: chess.Board) -> str:
  26. try:
  27. last_move = board.peek().uci()
  28. except IndexError:
  29. last_move = None
  30. # Current player color.
  31. player_color = "white" if board.turn == chess.WHITE else "black"
  32. user_color = "black" if player_color == "white" else "white"
  33. legal_moves = ", ".join([move.uci() for move in board.legal_moves])
  34. if last_move is None:
  35. prompt = f"New Game!\nBoard: {board.fen()}\nYou play {player_color}\nYour legal moves: {legal_moves}\n"
  36. else:
  37. prompt = f"Board: {board.fen()}\nYou play {player_color}\nUser ({user_color})'s last move: {last_move}\nYour legal moves: {legal_moves}\n"
  38. example_move = get_random_move(board)
  39. return (
  40. prompt
  41. + "Respond with this format: <move>{your move in UCI format}</move>. "
  42. + f"For example, <move>{example_move}</move>."
  43. )
  44. def get_user_prompt(board: chess.Board) -> str:
  45. try:
  46. last_move = board.peek().uci()
  47. except IndexError:
  48. last_move = None
  49. # Current player color.
  50. player_color = "white" if board.turn == chess.WHITE else "black"
  51. legal_moves = ", ".join([move.uci() for move in board.legal_moves])
  52. board_display = board.unicode(borders=True)
  53. if last_move is None:
  54. prompt = f"New Game!\nBoard:\n{board_display}\nYou play {player_color}\nYour legal moves: {legal_moves}\n"
  55. prompt = f"Board:\n{board_display}\nYou play {player_color}\nAI's last move: {last_move}\nYour legal moves: {legal_moves}\n"
  56. return prompt + "Enter your move in UCI format: "
  57. def extract_move(response: str) -> str:
  58. start = response.find("<move>") + len("<move>")
  59. end = response.find("</move>")
  60. if start == -1 or end == -1:
  61. raise ValueError("Invalid response format.")
  62. return response[start:end]
  63. async def get_ai_move(board: chess.Board, player: AssistantAgent, max_tries: int) -> str:
  64. task = get_ai_prompt(board)
  65. count = 0
  66. while count < max_tries:
  67. result = await Console(player.run_stream(task=task))
  68. count += 1
  69. assert isinstance(result.messages[-1], TextMessage)
  70. # Check if the response is a valid UC move.
  71. try:
  72. move = chess.Move.from_uci(extract_move(result.messages[-1].content))
  73. except (ValueError, IndexError):
  74. task = "Invalid format. Please read instruction.\n" + get_ai_prompt(board)
  75. continue
  76. # Check if the move is legal.
  77. if move not in board.legal_moves:
  78. task = "Invalid move. Please enter a move from the list of legal moves.\n" + get_ai_prompt(board)
  79. continue
  80. return move.uci()
  81. # If the player does not provide a valid move, return a random move.
  82. return get_random_move(board)
  83. async def main(human_player: bool, max_tries: int) -> None:
  84. board = chess.Board()
  85. # Load the model client from config.
  86. with open("model_config.yaml", "r") as f:
  87. model_config = yaml.safe_load(f)
  88. model_client = ChatCompletionClient.load_component(model_config)
  89. player = create_ai_player(model_client)
  90. while not board.is_game_over():
  91. # Get the AI's move.
  92. ai_move = await get_ai_move(board, player, max_tries)
  93. # Make the AI's move.
  94. board.push(chess.Move.from_uci(ai_move))
  95. # Check if the game is over.
  96. if board.is_game_over():
  97. break
  98. # Get the user's move.
  99. if human_player:
  100. user_move = input(get_user_prompt(board))
  101. else:
  102. user_move = get_random_move(board)
  103. # Make the user's move.
  104. board.push(chess.Move.from_uci(user_move))
  105. print("--------- User --------")
  106. print(user_move)
  107. print("-------- Board --------")
  108. print(board.unicode(borders=True))
  109. result = "AI wins!" if board.result() == "1-0" else "User wins!" if board.result() == "0-1" else "Draw!"
  110. print("----------------")
  111. print(f"Game over! Result: {result}")
  112. await model_client.close()
  113. if __name__ == "__main__":
  114. parser = argparse.ArgumentParser()
  115. parser.add_argument("--human", action="store_true", help="Enable human vs. AI mode.")
  116. parser.add_argument(
  117. "--max-tries", type=int, default=10, help="Maximum number of tries for AI input before a random move take over."
  118. )
  119. args = parser.parse_args()
  120. asyncio.run(main(args.human, args.max_tries))