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.2 kB

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