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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  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>")
  59. end = response.find("</move>")
  60. if start == -1 or end == -1:
  61. raise ValueError("Invalid response format.")
  62. if end < start:
  63. raise ValueError("Invalid response format.")
  64. return response[start+ len("<move>"):end].strip()
  65. async def get_ai_move(board: chess.Board, player: AssistantAgent, max_tries: int) -> str:
  66. task = get_ai_prompt(board)
  67. count = 0
  68. while count < max_tries:
  69. result = await Console(player.run_stream(task=task))
  70. count += 1
  71. assert isinstance(result.messages[-1], TextMessage)
  72. # Check if the response is a valid UC move.
  73. try:
  74. move = chess.Move.from_uci(extract_move(result.messages[-1].content))
  75. except (ValueError, IndexError):
  76. task = "Invalid format. Please read instruction.\n" + get_ai_prompt(board)
  77. continue
  78. # Check if the move is legal.
  79. if move not in board.legal_moves:
  80. task = "Invalid move. Please enter a move from the list of legal moves.\n" + get_ai_prompt(board)
  81. continue
  82. return move.uci()
  83. # If the player does not provide a valid move, return a random move.
  84. return get_random_move(board)
  85. async def main(human_player: bool, max_tries: int) -> None:
  86. board = chess.Board()
  87. # Load the model client from config.
  88. with open("model_config.yaml", "r") as f:
  89. model_config = yaml.safe_load(f)
  90. model_client = ChatCompletionClient.load_component(model_config)
  91. player = create_ai_player(model_client)
  92. while not board.is_game_over():
  93. # Get the AI's move.
  94. ai_move = await get_ai_move(board, player, max_tries)
  95. # Make the AI's move.
  96. board.push(chess.Move.from_uci(ai_move))
  97. # Check if the game is over.
  98. if board.is_game_over():
  99. break
  100. # Get the user's move.
  101. if human_player:
  102. user_move = input(get_user_prompt(board))
  103. else:
  104. user_move = get_random_move(board)
  105. # Make the user's move.
  106. board.push(chess.Move.from_uci(user_move))
  107. print("--------- User --------")
  108. print(user_move)
  109. print("-------- Board --------")
  110. print(board.unicode(borders=True))
  111. result = "AI wins!" if board.result() == "1-0" else "User wins!" if board.result() == "0-1" else "Draw!"
  112. print("----------------")
  113. print(f"Game over! Result: {result}")
  114. await model_client.close()
  115. if __name__ == "__main__":
  116. parser = argparse.ArgumentParser()
  117. parser.add_argument("--human", action="store_true", help="Enable human vs. AI mode.")
  118. parser.add_argument(
  119. "--max-tries", type=int, default=10, help="Maximum number of tries for AI input before a random move take over."
  120. )
  121. args = parser.parse_args()
  122. asyncio.run(main(args.human, args.max_tries))