def run(self, player, move, last_board, tree_level, is_max, tree_max_depth, use_memcache, alpha, beta, batch_size): new_board = ChessUtils.make_move(move, last_board) # Extract the set of possible moves for this level of the board. is_check, valid_moves = ChessUtils.get_valid_moves(player, new_board) # By default, continue recursing. end_recursion = False # Two recursion stop conditions. #------- Condition #1: Recursion Depth of maximum_depth of the tree. #------- Condition #2: No more valid player moves. if tree_level >= tree_max_depth or len(valid_moves) == 0: end_recursion = True # Ensure only one yield is generated in the case of recursions end so exit. if(end_recursion): yield common.Return((ChessUtils.get_state_utility(new_board, player, is_max, is_check, valid_moves), None)) # If child modes will be generated, then generate them as a fan-out then fan them back in. else: best_util_so_far = alpha if is_max else beta best_util_and_move = (best_util_so_far, None) yield MakeMoveIter(player, new_board, tree_level, is_max, tree_max_depth, use_memcache, alpha, beta, batch_size, best_util_and_move, valid_moves)
def run(self, player, board, tree_max_depth, use_memcache, alpha, beta, batch_size): # Extract the set of possible moves for this level of the board. is_check, valid_moves = ChessUtils.get_valid_moves(player, board) # Check that the game did not start in an invalid condition. if(is_check and len(valid_moves) == 0): raise ValueError("Game starting in checkmate. That is now allowed...") # If no moves were found to be valid, return an error message. if(len(valid_moves) == 0): yield common.Return("ERROR - NO VALID MOVES") # If only one move is possible, return that. elif(len(valid_moves) == 1): yield common.Return((0, valid_moves[0])) # If there are multiple valid moves, iterate through the possible valid moves. else: # Determine whether white or black is going first. if(player == PlayerType.WHITE): is_max = True else: is_max = False # Define the best move and util so far. best_util_so_far = alpha if is_max else beta best_util_and_move = (best_util_so_far, None) # Generate the Minimax tree. yield MakeMoveIter(player, board, 1, is_max, tree_max_depth, use_memcache, alpha, beta, batch_size, best_util_and_move, valid_moves)
def run(self, player, move, board, tree_level, is_max, depth_tree, batch_size, rationality_prob): if move: board = ChessUtils.make_move(move, board) # Extract the set of possible moves for this level of the board. is_check, valid_moves = ChessUtils.get_valid_moves(player, board) # By default, continue recursing. end_recursion = False # Two recursion stop conditions. # ------- Condition #1: Recursion Depth of 6 # ------- Condition #2: No more valid player moves. if tree_level >= depth_tree or len(valid_moves) == 0: end_recursion = True # Ensure only one yield is generated in the case of recursions end so exit. if end_recursion: yield common.Return((ChessUtils.get_state_utility(board, player, is_max, is_check, valid_moves), None)) return if len(valid_moves) == 1: yield common.Return( (ChessUtils.get_state_utility(board, player, is_max, is_check, valid_moves), valid_moves[0]) ) return next_player = 1 - player is_next_player_max = not is_max utilities_and_moves = [] for new_move in valid_moves: utility_and_move = yield InnerExpectiMaxMakeMove( next_player, new_move, board, tree_level + 1, is_next_player_max, depth_tree, # Required as part of the player increment. batch_size, rationality_prob, ) utilities_and_moves.append(utility_and_move) if tree_level == 2: yield DetermineExpectiMaxUtilAndMove(is_max, valid_moves, rationality_prob, *utilities_and_moves) else: yield DetermineBestUtilAndMove(is_max, valid_moves, *utilities_and_moves)
def query_server(current_player, board): """ Makes a request of the server to get the computer's move. Params: current_player : Integer - 0 for white and 1 for black. board - List of list of integers of the pieces on the board. Returns: Tuple of two integers. Index 0 in the Tuple is the current (i.e. source) location of the piece to be moved while index 1 is the new (i.e. destination) location of the piece. """ sys.stdout.write("The computer is thinking about its move ") # Create a queue to get the computers move. moveQueue = Queue.Queue() # Use a thread to get the player's move. #server_thread = threading.Thread(target=execute_server_command, args=(current_player, board, moveQueue, tree_max_depth, batch_size, rationality_prob, use_expectimax)) server_thread = threading.Thread(target=execute_server_command, args=(current_player, board, moveQueue, \ tree_max_depth, batch_size)) server_thread.start() # Wait for the thread to finish while(server_thread.is_alive()): sys.stdout.write("*****") time.sleep(1) sys.stdout.write("\n\n") # Handle the error case where the network communication crashed. if(moveQueue.empty()): sys.exit() computer_move = moveQueue.get(True) # Allow for two player debug if(two_player_debug): _, valid_moves = ChessUtils.get_valid_moves(current_player, board) computer_move = make_player_move(False, valid_moves, board) # Use player make move as a placeholder. return computer_move
def run(self, player, board, depth_tree, batch_size, rationality_prob): # Extract the set of possible moves for this level of the board. is_check, valid_moves = ChessUtils.get_valid_moves(player, board) if is_check and len(valid_moves) == 0: raise ValueError("Game starting in checkmate. That is now allowed...") # If no moves were found to be valid, return an error message. if len(valid_moves) == 0: yield common.Return("ERROR - NO VALID MOVES") # If only one move is possible, return that. elif len(valid_moves) == 1: yield common.Return((0, valid_moves[0])) # If there are multiple valid moves, iterate through the possible valid moves. else: # Determine whether white or black is going first. if player == PlayerType.WHITE: is_max = True else: is_max = False yield InnerExpectiMaxMakeMove(player, None, board, 1, is_max, depth_tree, batch_size)
def play_game(current_player, human_player, board): ''' Params: current_player: Integer - 0 or 1 to indicate whether it is white's or black's turn to start the game. human_player: Integer - 0 or 1 indicates whether the human player is white or black. This value should not change. board: List of lists of integers. It contains the current board. It will be updated at the end of each turn. Returns: This function runs and never returns. The program exits directly from this function. ''' while(True): check, valid_moves = ChessUtils.get_valid_moves(current_player, board) # Check if the game is over. if check and len(valid_moves) == 0: ChessUtils.print_board(board) print "\nCheckmate!" if current_player == human_player: print "The computer won! You lose!" else: print "You win!" sys.exit(0) # Check if the game is over due to stalemate. elif(not check and len(valid_moves) == 0): print "\nStalemate!\n" print "No moves possible so its a draw!" sys.exit(0) # Print the current board print "\n\nCurrent Board:" ChessUtils.print_board(board) # Human's turn. if(current_player == human_player): # Keep loop until the user enters a valid move. next_move = make_player_move(check, valid_moves, board) # Computer's turn. else: if check: print "The computer is in check. It must enter a move to exit check:\n" else: print "Its the computer's turn. It is deciding on its move.\n" next_move = query_server(current_player, board) # Print the computer's move. print "The computer's move is: (" + str(next_move[0]) + \ ", " + str(next_move[1]) + ")\n" board = ChessUtils.make_move(next_move, board) # Apply the move. current_player = 1 - current_player # update the player pass