Esempio n. 1
0
class StudentAI():

    def __init__(self, col, row, p):
        self.col = col
        self.row = row
        self.p = p
        self.board = Board(col, row, p)
        self.board.initialize_game()
        self.color = ''
        self.opponent = {1: 2, 2: 1}
        self.color = 2

    def get_move(self, move):
        if len(move) != 0:
            self.board.make_move(move, self.opponent[self.color])
        else:
            self.color = 1
        moves = self.board.get_all_possible_moves(self.color)
        best_move = moves[0][0]
        #self.board.make_move(best_move, self.color)
        #best_score = self.board_score( self.color )
        #self.board.undo()
        move = self.minMax(self.color, 3, -999999999, best_move, 999999999, best_move)[1]
        self.board.make_move(move, self.color)

        return move
    
    def minMax(self, player, depth, best_score, best_move, opponent_score, opponent_move):
        if depth == 0:
            return self.board_score( player ), best_move
        # get all the moves of the current player
        moves = self.board.get_all_possible_moves(player)
        # Itterate through each move
        for i in moves:
            for ii in i:
                # change to new game state
                self.board.make_move(ii, player)
                if (player == self.color):
                    opponent_score = self.minMax(self.opponent[self.color], depth-1, best_score, best_move,opponent_score, opponent_move)[0]
                    if (best_score <  opponent_score):
                        best_score = opponent_score
                        best_move = ii
                # opponent's turn: find the best score based on player's move
                elif (player == self.opponent[self.color]):
                    best_score = self.minMax(self.color, depth-1, best_score, best_move,opponent_score, opponent_move)[0]
                    if (opponent_score > best_score):
                        opponent_score = best_score
                        opponent_move = ii
                self.board.undo()
        return best_score, best_move, opponent_score, opponent_move

    def board_score(self, color):
        ## @param color: color of player making the move
        ## Heuristics to Evaluate with
        ## Normal Piece : 1000 pts
        ## King Piece : 2000 pts
        ## Rows away from enemy end if Normal : (rows - curr_row / rows) * 1000
        ## Amount of Pieces : (Amount of pieces left) / (self.col * self.p / 2) * 100
        ## Randomization : randomInt (0-10)

        player_points = 0
        opponent_points = 0
        for c in range(self.col):
            for r in range(self.row):
                current_piece = self.board.board[c][r]

                if current_piece.get_color() == color:
                    if current_piece.is_king == True:
                        player_points += 2000
                    else:
                        player_points += 1000
                        if color == 1:
                            player_points += ((self.row - r) / self.row) * 1000
                        else:
                            player_points += (r / self.row) * 1000
                elif current_piece.get_color() == self.opponent[color]:
                    if current_piece.is_king == True:
                        opponent_points += 2000
                    else:
                        opponent_points += 1000
                        if self.opponent[color] == 1:
                            opponent_points += ((self.row - r) / self.row) * 1000
                        else:
                            opponent_points += (r / self.row) * 1000
                else:
                    pass
        
        if color == 1:
            player_points += ((self.board.white_count / (self.col * self.p / 2)) * 100)
            opponent_points += ((self.board.black_count / (self.col * self.p / 2)) * 100)
        else:
            player_points += ((self.board.black_count / (self.col * self.p / 2)) * 100)
            opponent_points += ((self.board.white_count / (self.col * self.p / 2)) * 100)
        
        randomization = randint(0, 50)
            
        return player_points - opponent_points + randomization
Esempio n. 2
0
class StudentAI():
    def __init__(self, col, row, p):
        self.col = col
        self.row = row
        self.p = p
        self.board = Board(col, row, p)
        self.board.initialize_game()
        self.color = ''
        self.opponent = {1: 2, 2: 1}
        self.color = 2
        # Returns optimal value for current player

    def get_move(self, move):
        alpha = -1000
        value = -1000
        beta = 1000
        bestMove = None

        if len(move) != 0:  # If the opponent started first
            self.board.make_move(move, self.opponent[self.color])
        else:
            self.color = 1

        # Make a list of all possible moves that our AI can make
        our_moves = self.board.get_all_possible_moves(self.color)

        # Iterate through list of all our moves
        for x in range(len(our_moves)):
            for y in range(len(our_moves[x])):
                # Make a move on the copy/theoretical board
                self.board.make_move(our_moves[x][y], self.color)
                currentScore = self.alphaBetaMin(alpha, beta, 1)
                self.board.undo()

                if currentScore >= value:
                    value = currentScore
                    bestMove = our_moves[x][y]
                    #print("New bestMove", bestMove, "current best score:", currentScore)
                    alpha = currentScore

        #print("Decision?", bestMove)
        self.board.make_move(bestMove, self.color)
        return bestMove

    def alphaBetaMin(self, alpha, beta, depth):
        '''
        # Check if our AI is black and we won
        #if self.color == self.board.is_win(self.color):
        if self.color == self.board.is_win("B"):
            return 1000
        # Check if our AI (black) lost
        #elif self.color == 1 and self.board.is_win(self.color) == 2:
        elif self.color == 1 and self.board.is_win("B") == 2:
            return -1000
        # Check if our AI (white) lost
        #elif self.color == 2 and self.board.is_win(self.color) == 1:
        elif self.color == 2 and self.board.is_win("W") == 1:
            return -1000
        
        # Check if opponent will tie
        #if self.board.is_win(self.color) == -1:
        if self.board.is_win("B") == -1:
            return 0
        '''
        if depth == 3:
            return self.get_heuristic_score2()
        else:
            value = 1000
            # Go through every possible move
            opponent_moves = self.board.get_all_possible_moves(
                self.opponent[self.color])
            for x in opponent_moves:
                for move in x:
                    # Make move for opponent
                    self.board.make_move(move, self.opponent[self.color])
                    value = min(value,
                                self.alphaBetaMax(alpha, beta, depth + 1))
                    self.board.undo()
                    beta = min(beta, value)
                    if alpha >= beta:
                        return value
            return value

    def alphaBetaMax(self, alpha, beta, depth):
        '''
        # Check if our AI is black and we won
        #if self.color == self.board.is_win(self.opponent[self.color]):
        if self.color == self.board.is_win("B"):
            return 1000
        # Check if our AI (black) lost
        #elif self.color == 1 and self.board.is_win(self.opponent[self.color]) == 2:
        elif self.color == 1 and self.board.is_win("B") == 2:
            return -1000
        # Check if our AI (white) lost
        #elif self.color == 2 and self.board.is_win(self.opponent[self.color]) == 1:
        elif self.color == 2 and self.board.is_win("W") == 1:
            return -1000
        
        # Check if opponent will tie
        #if self.board.is_win(self.opponent[self.color]) == -1:
        if self.board.is_win("B") == -1:
            return 0
        '''
        if depth == 3:
            return self.get_heuristic_score2()
        else:
            value = -1000
            # Go through every possible move
            our_moves = self.board.get_all_possible_moves(self.color)
            for x in our_moves:
                for move in x:
                    self.board.make_move(move, self.color)
                    value = max(value,
                                self.alphaBetaMin(alpha, beta, depth + 1))
                    self.board.undo()
                    alpha = max(alpha, value)
                    if alpha >= beta:
                        return value
            return value

    def closeToBecomingKing(self, color, row_position):
        if self.color == 1:  # Our color is black
            return row_position
        else:  # our color is white
            return (self.board.row - row_position - 1)

    def get_heuristic_score2(self):

        num_black_kings = 0
        num_white_kings = 0
        num_safe_piece_black = 0
        num_safe_piece_white = 0
        num_back_black = 0
        num_back_white = 0
        closer_black = 0
        closer_white = 0
        #score = 0
        for x in range(len(self.board.board)):
            for y in range(len(self.board.board[x])):
                # Check if it's our checker piece
                if (self.board.board[x][y].get_color() == 'B'):
                    # Check if it's a king
                    if (self.board.board[x][y].is_king == True):
                        num_black_kings += 1
                    else:  # Check how close checker piece is to becoming King
                        closer_black += self.closeToBecomingKing(self.color, x)

                    cp = self.board.board[x][y].get_location()

                    # Check if black checker piece is in the back
                    if (cp[0] == 0):
                        num_back_black += 1

                    # Check if it's an edge piece row 0, row n, col 0, col n
                    if (cp[0] == 0 or cp[0] == self.board.row - 1):
                        num_safe_piece_black += 1
                    if (cp[1] == 0 or cp[1] == self.board.col - 1):
                        num_safe_piece_black += 1
                    if (cp[0] == 0 and cp[1] == 0):
                        num_safe_piece_black -= 1
                    if (cp[0] == 0 and cp[1] == self.board.col - 1):
                        num_safe_piece_black -= 1
                    if (cp[0] == self.board.row - 1 and cp[1] == 0):
                        num_safe_piece_black -= 1
                    if (cp[0] == self.board.row - 1
                            and cp[1] == self.board.col - 1):
                        num_safe_piece_black -= 1

                    # Check for safe pieces that are not part of the edge
                    if (cp[0] != 0 and cp[0] != self.board.row - 1):
                        if (cp[1] != 0 and cp[1] != self.board.col - 1):
                            is_safe = True
                            if (self.board.board[x + 1][y -
                                                        1].get_color() == 'W'):
                                if (self.board.board[x -
                                                     1][y +
                                                        1].get_color() == '.'):
                                    is_safe = False
                            if (self.board.board[x + 1][y +
                                                        1].get_color() == 'W'):
                                if (self.board.board[x -
                                                     1][y -
                                                        1].get_color() == '.'):
                                    is_safe = False
                            if (self.board.board[x - 1][y + 1].get_color()
                                    == 'W' and
                                    self.board.board[x - 1][y + 1].is_king):
                                if (self.board.board[x +
                                                     1][y -
                                                        1].get_color() == '.'):
                                    is_safe = False
                            if (self.board.board[x - 1][y - 1].get_color()
                                    == 'W' and
                                    self.board.board[x - 1][y - 1].is_king):
                                if (self.board.board[x +
                                                     1][y +
                                                        1].get_color() == '.'):
                                    is_safe = False
                            if (is_safe == True):
                                #print("safe piece counted")
                                num_safe_piece_black += 1
                            #else:
                            #print(x, y)
                            #print("safe piece not counted")
                            #score -= 2
                    '''
                    # Check for safe pieces that are part of the edges
                    is_safe = True
                    # Check for safe piece on edge (column - 1)
                    if (cp[1] == self.board.col - 1):
                        if(self.board.board[x + 1][y - 1].get_color() == 'W'):
                            is_safe = False
                        # Check for safe piece on edge (0)
                    if (cp[1] == 0):
                        if(self.board.board[x + 1][y + 1].get_color() == 'W'):
                            is_safe = False
                    # check for safe piece on edge (column - 1) when a King
                    if (cp[1] == self.board.col - 1 and ((cp[0] > 0) or (cp[0] < self.board.row - 1))):
                        if(self.board.board[x - 1][y - 1].get_color() == 'W'):
                            is_safe = False
                        if(self.board.board[x + 1][y - 1].get_color() == 'W'):
                            is_safe = False
                        # check for safe piece on edge (0) when a King
                        if (cp[1] == 0 and ((cp[0] > 0) or (cp[0] < self.board.row - 1))):
                            if(self.board.board[x - 1][y + 1].get_color() == 'W'):
                                is_safe = False
                            if(self.board.board[x + 1][y + 1].get_color() == 'W'):
                                is_safe = False
                    
                        if (is_safe == True):
                            num_safe_piece_black += 1
                    '''

                elif (self.board.board[x][y].get_color() == 'W'):
                    if (self.board.board[x][y].is_king == True):
                        num_white_kings += 1
                    else:
                        closer_white += self.closeToBecomingKing(2, x)

                    # Check if it's a corner piece either (0, 0), (0, n), (n, 0), or (n, n)
                    cp = self.board.board[x][y].get_location()

                    # Check if white checker piece is in the back
                    if (cp[0] == self.board.row - 1):
                        num_back_white += 1
                    # Check if it's an edge piece row 0, row n, col 0, col n
                    if (cp[0] == 0 or cp[0] == self.board.row - 1):
                        num_safe_piece_white += 1
                    if (cp[1] == 0 or cp[1] == self.board.col - 1):
                        num_safe_piece_white += 1
                    if (cp[0] == 0 and cp[1] == 0):
                        num_safe_piece_white -= 1
                    if (cp[0] == 0 and cp[1] == self.board.col - 1):
                        num_safe_piece_white -= 1
                    if (cp[0] == self.board.row - 1 and cp[1] == 0):
                        num_safe_piece_white -= 1
                    if (cp[0] == self.board.row - 1
                            and cp[1] == self.board.col - 1):
                        num_safe_piece_white -= 1
                    # Check for white safe pieces that are not part of the edge
                    if (cp[0] != 0 and cp[0] != self.board.row - 1):
                        if (cp[1] != 0 and cp[1] != self.board.col - 1):
                            is_safe = True
                            if (self.board.board[x - 1][y -
                                                        1].get_color() == 'B'):
                                if (self.board.board[x +
                                                     1][y +
                                                        1].get_color() == '.'):
                                    is_safe = False
                            if (self.board.board[x - 1][y +
                                                        1].get_color() == 'B'):
                                if (self.board.board[x +
                                                     1][y -
                                                        1].get_color() == '.'):
                                    is_safe = False
                            if (self.board.board[x + 1][y + 1].get_color()
                                    == 'B' and
                                    self.board.board[x + 1][y + 1].is_king):
                                if (self.board.board[x -
                                                     1][y -
                                                        1].get_color() == '.'):
                                    is_safe = False
                            if (self.board.board[x + 1][y - 1].get_color()
                                    == 'B' and
                                    self.board.board[x + 1][y - 1].is_king):
                                if (self.board.board[x -
                                                     1][y +
                                                        1].get_color() == '.'):
                                    is_safe = False
                            if (is_safe == True):
                                num_safe_piece_white += 1
        if self.color == 1:
            score = 10 * (self.board.black_count - self.board.white_count)
            #print("Score after diff in counts:", score)
            #print('safe black:', num_safe_piece_black, 'safe white:', num_safe_piece_white, 'safe score:', num_safe_piece_black - num_safe_piece_white)
            score += 5 * (num_black_kings - num_white_kings)
            #print("Score after diff in Ks:", score)
            #score += 2*(closer_black - closer_white)
            score += 2 * (num_safe_piece_black - num_safe_piece_white)
            #print("Score after diff in safe pieces:", score)
            score += 2 * (num_back_black - num_back_white)
            #print("Score after back row pieces:", score)
        elif self.color == 2:
            score = 10 * (self.board.white_count - self.board.black_count)
            #print("Score after diff in counts:", score)
            #print('safe black:', num_safe_piece_black, 'safe white:', num_safe_piece_white, 'safe score:', num_safe_piece_black - num_safe_piece_white)
            score += 5 * (num_white_kings - num_black_kings)
            #print("Score after diff in Ks:", score)
            #score += 2*(closer_black - closer_white)
            score += 2 * (num_safe_piece_white - num_safe_piece_black)
            #print("Score after diff in safe pieces:", score)
            score += 2 * (num_back_white - num_back_black)
            #print("Score after back row pieces:", score)
        return score
Esempio n. 3
0
class StudentAI():
    def __init__(self, col, row, p):
        self.col = col
        self.row = row
        self.p = p
        self.board = Board(col, row, p)
        self.board.initialize_game()
        self.color = ''
        self.opponent = {1: 2, 2: 1}
        self.color = 2
        self.search_lim = 5
        self.current_node = TreeNode(None, self.color)

    def get_move(self, move):
        if len(move) != 0:
            # print("|" + str(move) + "|")
            self.board.make_move(move, self.opponent[self.color])
            #print("Player", self.opponent[self.color], "make move", move)
            if len(self.current_node.child_node) != 0:
                for child in self.current_node.child_node:
                    if str(child.move) == str(move):
                        self.current_node = child
        else:
            self.color = 1
            self.current_node.player = self.color
        for i in range(NS):
            self.mcts(self.current_node)
            #self.board.show_board()
            #print("mcts counter:", i)
        move = self.current_node.child_node[0]
        for child in self.current_node.child_node:
            if move.uct() < child.uct():
                move = child
        self.board.make_move(move.move, self.color)
        # print("Player", self.color, "make move", move.move, "with a winrate of", move.winrate(), "simulated", move.simulation)
        self.current_node = move
        return move.move

    def mcts(self, node):
        if node.simulation >= minVisit:
            #print("depth:", depth)
            node.simulation += 1
            if not len(node.child_node):
                moves = self.board.get_all_possible_moves(node.player)
                for move in moves:
                    for eachmove in move:
                        node.child_node.append(
                            TreeNode(eachmove, self.opponent[node.player],
                                     node))
            # proceed
            next = self.mcts_selection(node)
            self.board.make_move(next.move, node.player)
            result = self.board.is_win(node.player)
            if result:
                if result == self.opponent[node.player]: node.win += 1
                elif result == node.player:
                    next.win += 1
                    next.simulation += 1
                self.board.undo()
                return result
                #self.board.show_board()
            result = self.mcts(next)
            self.board.undo()
            # propagate up
            if result == self.opponent[node.player]:
                node.win += 1
            return result
        else:
            result = self.simulate(node.player)
            node.simulation += 1
            if result == self.opponent[node.player]:
                node.win += 1
            #print("simulating", result)
            return result

    def mcts_selection(self, node):  # Select optimal UCB node
        current = node.child_node[0]
        for child in node.child_node:
            #print(current.uct())
            if current.uct() < child.uct():
                current = child
        #print("player", node.player, "pick", current.move)
        return current

    def simulate(self, player):
        win = 0
        counter = 0
        fake_board = Board(self.col, self.row, self.p)
        self.copy_board(fake_board)
        # print("DIT ME DIEN")
        # fake_board.show_board()
        # totaltime = 0
        while win == 0:
            moves = fake_board.get_all_possible_moves(player)
            if len(moves) == 1:
                index = 0
            elif len(moves) == 0:
                win = self.opponent[player]
                break
            else:
                index = randint(0, len(moves) - 1)
            if len(moves[index]) == 1:
                inner_index = 0
            else:
                inner_index = randint(0, len(moves[index]) - 1)
            move = moves[index][inner_index]
            fake_board.make_move(move, player)
            counter += 1
            # bt = time.time()
            if fake_board.tie_counter >= fake_board.tie_max:
                win = -1
            # totaltime += time.time() - bt
            # print("self.board.is_win():", time.time() - bt)
            player = self.opponent[player]

        # #print("total time is_win:", totaltime)
        # #bt = time.time()
        # for i in range(counter):
        #     self.board.undo()
        # #rint("total time undo:", time.time() - bt)
        # fake_board.show_board()
        return win

    def copy_board(self, board):
        """
        EZ game
        :return: ez board
        """
        board.tie_counter = self.board.tie_counter
        board.tie_max = self.board.tie_max
        board.board = copy.deepcopy(self.board.board)
        board.saved_move = copy.deepcopy(self.board.saved_move)
        board.black_count = self.board.black_count
        board.white_count = self.board.white_count
Esempio n. 4
0
class StudentAI():
    def __init__(self, col, row, p):
        self.col = col
        self.row = row
        self.p = p
        self.board = Board(col, row, p)
        self.board.initialize_game()
        self.color = ''
        self.opponent = {1: 2, 2: 1}
        self.color = 2

    def evalFunction(self, newBoard, color):
        blackScore = 0
        whiteScore = 0
        totalPieces = 0
        for x in range(0, newBoard.row):
            for y in range(0, newBoard.col):
                checker = newBoard.board[x][y]
                if checker.get_color() == 'W':
                    if checker.is_king:
                        whiteScore += 5 + newBoard.row + 2
                        totalPieces += 1
                    else:
                        whiteScore += 5 + checker.row
                        totalPieces += 1
                    if checker.row == 0 or checker.row == newBoard.row:
                        whiteScore += 5

                elif checker.get_color() == 'B':
                    if checker.is_king:
                        blackScore += 5 + newBoard.row + 2
                        totalPieces += 1
                    else:
                        blackScore += 5 + (newBoard.row - checker.row)
                        totalPieces += 1
                    if checker.col == 0 or checker.col == newBoard.col:
                        blackScore += 5
        if color == 1:
            return (blackScore - whiteScore) / totalPieces
        else:
            return (whiteScore - blackScore) / totalPieces

    def alphaBeta(self, newBoard, depth, alpha, beta, maxPlayer):
        # myScore = -9999
        # enemyScore = 9999
        #
        # if maxPlayer:
        #     prevTurn = self.opponent[self.color]
        # else:
        #     prevTurn = self.color

        # if(newBoard.is_win(prevTurn)) or (newBoard.is_win(prevTurn) == 0):
        #     print(newBoard.is_win(prevTurn))

        if depth == 0:
            # return the score
            # if maxPlayer:
            score = self.evalFunction(newBoard, self.color)
            return score  # return the heuristic value of the board.
        else:

            # if it is max player, then for every child, choose the one with the max score
            if maxPlayer:
                score = -9999
                # newMoves gets all possible moves (child nodes)
                newMoves = newBoard.get_all_possible_moves(self.color)
                for elem in newMoves:
                    for move in elem:
                        # print("max making move: ", move)
                        newBoard.make_move(move, self.color)
                        score = max(
                            score,
                            self.alphaBeta(newBoard, depth - 1, alpha, beta,
                                           False))

                        # if score >= myScore:
                        #     myScore = score
                        # score = max(score, self.alphaBeta(newBoard, depth - 1, alpha, beta, False))
                        alpha = max(alpha, score)
                        # prunes
                        if alpha >= beta:  # beta cut off
                            # print("undo: ", move)
                            newBoard.undo()
                            break
                        # print("undo: ", move)
                        newBoard.undo()
                return score

            else:
                # if min player, choose the minimum score
                score = 9999
                newMoves = newBoard.get_all_possible_moves(
                    self.opponent[self.color])

                for elem in newMoves:
                    for move in elem:
                        # print("min making move: ", move)
                        newBoard.make_move(move, self.opponent[self.color])
                        score = min(
                            score,
                            self.alphaBeta(newBoard, depth - 1, alpha, beta,
                                           True))
                        # score = min(score, self.alphaBeta(newBoard, depth - 1, alpha, beta, True))
                        # if score <= enemyScore:
                        #     enemyScore = score
                        beta = min(beta, score)
                        # prunes
                        if alpha >= beta:  # alpha cut off
                            # print("undo: ", move)
                            newBoard.undo()
                            break
                        # print("undo: ", move)
                        newBoard.undo()
                return score

    def get_move(self, move):
        if len(move) != 0:
            self.board.make_move(move, self.opponent[self.color])
        else:
            self.color = 1

        moves = self.board.get_all_possible_moves(self.color)
        bestScore = -9999
        bestMove = Move([])
        for elem in moves:
            for move in elem:

                # for elem in move:
                # make deep copy of board
                # print("max making move: ", move)
                self.board.make_move(move, self.color)
                score = self.alphaBeta(self.board, 3, -9999, 9999, False)
                # print("score: ", score)
                if score >= bestScore:
                    bestScore = score
                    bestMove = move
                # print("undo: ", move)
                self.board.undo()
        # print("best move:", bestMove)
        print("best score: ", bestScore)
        self.board.make_move(bestMove, self.color)
        return bestMove
Esempio n. 5
0
class StudentAI():
    
    def __init__(self,col,row,p):
        self.col = col
        self.row = row
        self.p = p
        self.board = Board(col,row,p)
        self.board.initialize_game()
        self.color = ''
        self.opponent = {1:2,2:1}
        self.color = 2

    def get_move(self,move):
        if len(move) != 0:
            self.board.make_move(move,self.opponent[self.color])
        else:
            self.color = 1

        bestVal = -999
        bestMove = None

        # if there is only one move to make, just make the move without evaluating
        possible_moves = self.board.get_all_possible_moves(self.color)
        if len(possible_moves) == 1 and len(possible_moves[0]) == 1:
            self.board.make_move(possible_moves[0][0], self.color)
            return possible_moves[0][0]

        for moves in possible_moves:
            for move in moves:
                self.board.make_move(move, self.color)
                val = self.search(1, StudentAI.switchColors(self.color), MIN, MAX)
                self.board.undo()

                if val > bestVal:
                    bestVal = val
                    bestMove = move

        self.board.make_move(bestMove, self.color)
        return bestMove

    def search(self, depth, currentColor, alpha, beta):
        if depth == 4 or self.board.is_win('B') or self.board.is_win('W'):
            return self.evaluate(currentColor)

        best = MIN if currentColor == self.color else MAX

        for moves in self.board.get_all_possible_moves(currentColor):
            for move in moves:
                self.board.make_move(move, currentColor)
                val = self.search(depth+1, StudentAI.switchColors(currentColor), alpha, beta)
                self.board.undo()
                
                if currentColor == self.color:
                    best = max(best, val)
                    alpha = max(alpha, best)

                elif currentColor != self.color:
                    best = min(best, val)
                    beta = min(beta, best)

                if beta <= alpha:
                    return best

        return best

    def piece_differential(self, currentColor):
        if currentColor == 'B':
            return self.board.black_count - self.board.white_count
        return self.board.white_count - self.board.black_count

    def evaluate(self, currentColor):
        currentColor = 'B' if currentColor == 1 else 'W'
        oppColor = 'W' if currentColor == 'B' else 'B'
        # if we win in this game state, prefer to choose this path
        # if the opponent wins in this game state, stay away from this path
        if self.board.is_win(currentColor):
            return 500
        elif self.board.is_win(oppColor):
            return -500

        piece_location, kings = 0, 0

        for i in range(self.board.row):
            for j in range(self.board.col):
                if (self.board.board[i][j].color == currentColor):
                    if self.board.board[i][j].is_king:
                        kings += 1
                        # we prefer the king to be in the middle of the board
                        if i <= self.row / 2:
                            piece_location += 7 + i
                        else:
                            piece_location += 7 + (self.board.row - i - 1)
                    else:
                        # we prefer the pawns to go to the opponent's side of the board
                        if self.board.board[i][j].color == 'B':
                            piece_location += 5 + i
                        else:
                            piece_location += 5 + (self.board.row - i - 1)
                elif (self.board.board[i][j].color == oppColor):
                    if self.board.board[i][j].is_king:
                        kings -= 1
                        # we prefer the opponent's king to not be in the middle of the board
                        if i <= self.row / 2:
                            piece_location -= 7 + i
                        else:
                            piece_location -= 7 + (self.board.row - i - 1)
                    else:
                        # we prefer the opponent's pawns to not be on our side of the board
                        if self.board.board[i][j].color == 'B':
                            piece_location -= 5 + i
                        else:
                            piece_location -= 5 + (self.board.row - i - 1)

        # if we have more kings, we prefer to play more aggressive
        if kings > 0:
            return piece_location + self.board.row * self.piece_differential(currentColor)
        else:
            return piece_location + self.piece_differential(currentColor)

    @staticmethod
    def switchColors(color):
        if color == 1:
            return 2
        return 1
class StudentAI():
    def __init__(self, col, row, p):
        self.col = col
        self.row = row
        self.p = p
        self.board = Board(col, row, p)
        self.board.initialize_game()
        self.color = ''
        self.opponent = {1: 2, 2: 1}
        self.color = 2
        self.movecount = 0
        self.simulate_times = 100
        # self.file = f"{self.col}-{self.row}-{self.color}-data.txt"
        # self.file = open(f"{self.col}-{self.row}-data.txt", "a")

    def get_move(self, move):
        self.movecount += 1
        if len(move) != 0:
            self.board.make_move(move, self.opponent[self.color])
        else:
            self.color = 1
        self.file = f"{self.col}-{self.row}-{self.color}-data.txt"
        moves = self.get_moves(self.board, self.color)
        move = self.monte_carlo_tree(moves, self.simulate_times)
        self.board.make_move(move, self.color)
        return move

    def monte_carlo_tree(self, moves: [], simulate_times: int):
        s_parent = simulate_times * len(moves)
        best_uct = -math.inf
        best_move = 0
        for move in moves:
            wins = self.simulate(move, simulate_times)
            uct = wins / simulate_times + math.sqrt(
                2 * math.log(s_parent) / simulate_times)
            if uct > best_uct:
                best_move = move
        index = randint(0, len(moves) - 1)
        move = moves[index]
        return move

    def simulate(self, move, simulate_times):
        win = 0
        self.board.make_move(move, self.color)
        for _ in range(simulate_times):
            curr_turn = self.opponent[self.color]
            t = 0
            moves = self.get_moves(self.board, curr_turn)
            while len(moves) > 0 and t < 50:
                move = self.rollout(moves)
                self.board.make_move(move, curr_turn)
                curr_turn = self.opponent[curr_turn]
                moves = self.get_moves(self.board, curr_turn)
                t += 1
            win += 1 if curr_turn != self.color else 0 if t != 50 else 0.5
            self.undo(self.board, t)
        print(win / simulate_times * 100)
        bf, wf = self.board_to_feature(self.board, self.color)
        self.write_to_file(bf, wf, win / simulate_times * 100)
        self.board.undo()
        return win

    def board_to_feature(self, board, color):

        # result = ""
        # result += f"{board.white_count/self.total} {board.black_count/self.total} "
        #
        # wking,bking  = self.wking_bking(board)
        # result += f"{wking/self.total} {bking/self.total} "
        #
        # wback, bback = self.wback_bback(board)
        # result += f"{wback/self.total} {bback/self.total} "
        #
        # wedge, bedge = self.wedge_bedge(board)
        # result += f"{wedge/self.total} {bedge/self.total} "
        #
        # wdiagonal, bdiagonal = self.wdiagonal_bdiagonal(board)
        # result += f"{wdiagonal/self.total} {bdiagonal/self.total} "
        #
        # wdis, bdis = self.wdis_bdis(board)
        # result += f"{wdis/self.total} {bdis/self.total} "
        #
        # result += str(self.movecount)

        wking, bking = self.wking_bking(board)
        wcount, bcount = self.wcount_bcount(board)
        wdis, bdis = self.wdis_bdis(board)
        wedge, bedge = self.wedge_bedge(board)
        wcenter, bcenter = self.wcenter_bcenter(board)
        wback, bback = self.wback_bback(board)

        wdiag, bdiag = self.wdiag_bdiag(board)
        wdog, bdog = self.wdog_bdog(board)
        wbridge, bbridge = self.wbridge_bbridge(board)
        wuptriangle, buptriangle = self.wuptriangle_buptriangle(board)
        wdowntriangle, bdowntriangle = self.wdowntriangle_bdowntriangle(board)
        woreo, boreo = self.woreo_boreo(board)

        if self.color == 1:
            wmoveable, weatable = self.moveables(board, 2)
            bmoveable, beatable = 0, 0
        else:
            wmoveable, weatable = 0, 0
            bmoveable, beatable = self.moveables(board, 1)

        return [wcount, wking, wdis, wback, wedge,
                wcenter, wdiag, wdog, wbridge, wuptriangle,
                wdowntriangle, woreo, wmoveable, weatable],\
                [bcount, bking, bdis, bback, bedge,
                 bcenter, bdiag, bdog, bbridge, buptriangle,
                 bdowntriangle, boreo, bmoveable, beatable]

    def wcount_bcount(self, board):
        return board.white_count, board.black_count

    def wking_bking(self, board):
        bking, wking = 0, 0
        for r in range(self.board.row):
            for c in range(self.board.col):
                if self.board.board[r][c].color == "B":
                    bking += self.board.board[r][c].is_king
                elif self.board.board[r][c].color == "W":
                    wking += self.board.board[r][c].is_king
        return wking, bking

    def moveables(self, board, color):
        moves = [
            m for chess in board.get_all_possible_moves(color) for m in chess
        ]
        eatable = 0
        for m in moves:
            if len(m.seq) > 2:
                eatable += (len(m.seq) - 1)
                continue
            if math.sqrt((m.seq[0][0] - m.seq[1][0])**2 +
                         (m.seq[0][1] - m.seq[1][1])**2) > 1:
                eatable += 1
        # print(f"len(moves): {len(moves)}, eatable: {eatable}")
        return len(moves), eatable

    def wback_bback(self, board):
        bback = sum(board.board[0][i].color == "B" for i in range(board.col))
        wback = sum(board.board[board.row - 1][i].color == "W"
                    for i in range(board.col))
        return wback, bback

    def wedge_bedge(self, board):
        bedge = sum((board.board[i][0].color == "B") +
                    (board.board[i][board.col - 1].color == "B")
                    for i in range(board.row))
        wedge = sum((board.board[i][0].color == "W") +
                    (board.board[i][board.col - 1].color == "W")
                    for i in range(board.row))
        # print(f"wedge: {wedge}, bedge: {bedge}")
        return wedge, bedge

    def wcenter_bcenter(self, board):
        wcenter = sum((board.board[int(board.row/2)][i].color =="W")+ \
                      (board.board[int(board.row/2)+1][i].color =="W") for i in range(board.col))
        bcenter = sum((board.board[int(board.row/2)][i].color == "B")+ \
                      (board.board[int(board.row/2)+1][i].color =="B") for i in range(board.col))
        # print(f"wcenter: {wcenter}, bcenter: {bcenter}")
        return wcenter, bcenter

    def wdiagonal_bdiagonal(self, board):
        bdiagonal = sum(board.board[i][i].color == "B"  for i in range(board.row//4, 3*board.row//4)) + \
                    sum(board.board[board.row - 1 - i][board.row - 1 - i].color == "B"  for i in range(board.row))
        wdiagonal = sum(board.board[i][i].color == "W"  for i in range(board.row)) + \
                    sum(board.board[board.row - 1 - i][board.row - 1 - i].color == "W" for i in range(board.row))
        # print(f"wdiagonal: {wdiagonal}, bdiagonal: {bdiagonal}")
        return wdiagonal, bdiagonal

    def wdiag_bdiag(self, board):
        bc, wc = 0, 0
        for r in range(board.row - 1):
            bc += (board.board[r][r].color == "B") + (board.board[r+1][r].color == "B") + (board.board[r][r+1].color == "B") \
                + (board.board[r][board.col-1-r].color == "B") + (board.board[r+1][board.col-1-r].color == "B") +\
                   (board.board[r][board.col-2-r].color == "B")

            wc += (board.board[r][r].color == "W") + (board.board[r + 1][r].color == "W") + (board.board[r][r + 1].color == "W")\
                + (board.board[r][board.col-1-r].color == "W") + (board.board[r+1][board.col-1-r].color == "W") +\
                   (board.board[r][board.col-2-r].color == "W")
        bc += (board.board[board.row - 1][0].color == "B") + (
            board.board[board.row - 1][board.row - 1].color == "B")
        wc += (board.board[board.row - 1][0].color == "W") + (
            board.board[board.row - 1][board.row - 1].color == "W")

        # print(f"wdiag: {wc}, bdiag: {bc}")
        return wc, bc

    def wdog_bdog(self, board):
        wc = (board.board[board.row-1][board.col-1].color == "." and board.board[board.row-1][board.col-2].color == "W" \
            and board.board[board.row-2][board.col-1].color == "B") +\
             (board.board[board.row-1][0].color == "." and board.board[board.row-1][1].color == "W"\
            and board.board[board.row-2][0].color == "B")

        bc = (board.board[0][0].color == "." and board.board[0][1].color == "B" \
             and board.board[1][0].color == "W") + \
              (board.board[0][board.col-1].color == "." and board.board[0][board.col-2].color == "B" \
             and board.board[1][board.col-1].color == "W")
        # print(f"wdog: {wc}, bdog: {bc}")
        return wc, bc

    def wbridge_bbridge(self, board):
        bc = sum(
            board.board[0][c].color == "B" and board.board[0][c +
                                                              2].color == "B"
            for c in range(1, board.col - 3))
        wc = sum(board.board[board.row - 1][c].color == "W"
                 and board.board[board.row - 1][c + 2].color == "W"
                 for c in range(1, board.col - 3))
        # print(f"wbridge: {wc}, bbridge: {bc}")
        return wc, bc

    def wuptriangle_buptriangle(self, board):
        bcount, wcount = 0, 0
        for r in range(1, board.row - 1):
            for c in range(board.col - 2):
                if board.board[r][c].color == "B" and board.board[r - 1][
                        c + 1].color == "B" and board.board[r][c +
                                                               2].color == "B":
                    bcount += 1
                if board.board[r][c].color == "W" and board.board[r - 1][
                        c + 1].color == "W" and board.board[r][c +
                                                               2].color == "W":
                    wcount += 1
        # print(f"wuptriangle: {wcount}, buptriangle: {bcount}")
        return wcount, bcount

    def wdowntriangle_bdowntriangle(self, board):
        bcount, wcount = 0, 0
        for r in range(board.row - 1):
            for c in range(board.col - 2):
                if board.board[r][c].color == "B" and board.board[r + 1][
                        c + 1].color == "B" and board.board[r][c +
                                                               2].color == "B":
                    bcount += 1
                if board.board[r][c].color == "W" and board.board[r + 1][
                        c + 1].color == "W" and board.board[r][c +
                                                               2].color == "W":
                    wcount += 1
        # print(f"wdowntriangle: {wcount}, bdowntriangle: {bcount}")
        return wcount, bcount

    def woreo_boreo(self, board):
        '''
        :param board:
        :return: triangle pattern in the last row
        '''
        boreo = sum(board.board[0][c].color == "B" and board.board[1][c+1].color == "B" \
                    and board.board[0][c+2].color == "B" for c in range(0, board.col-2))
        woreo = sum(board.board[board.row-1][c].color == "W" and board.board[board.row-2][c+1].color == "W" \
                    and board.board[board.row-1][c+2].color == "W" for c in range(0, board.col-2))
        # print(f"woreo: {woreo}, boreo: {boreo}")
        return woreo, boreo

    def wdis_bdis(self, board):
        wdis = sum(board.row - 1 - i for i in range(board.row)
                   for j in range(board.col) if board.board[i][j].color == "W")
        bdis = sum(i for i in range(board.row) for j in range(board.col)
                   if board.board[i][j].color == "B")
        return wdis, bdis

    ######### help function #########
    def rollout(self, moves):
        '''Random roll a move from moves'''
        return moves[randint(0, len(moves) - 1)]

    def get_moves(self, board, turn):
        return [
            m for chess in board.get_all_possible_moves(turn) for m in chess
        ]

    def undo(self, board, times):
        for _ in range(times):
            board.undo()

    def write_to_file(self, wfeatures, bfeatures, win_rate):
        with open(self.file, "a") as f:
            w = ' '.join(str(x) for x in wfeatures)
            b = ' '.join(str(x) for x in bfeatures)
            f.write(w + ' ' + b + ' ' + str(win_rate) + '\n')
Esempio n. 7
0
class StudentAI():

    def __init__(self,col,row,p):
        self.col = col
        self.row = row
        self.p = p
        self.board = Board(col,row,p)
        self.board.initialize_game()
        self.color = ''
        self.opponent = {1:2,2:1}
        self.color = 2

    def get_move(self,move):
        if len(move) != 0:
            self.board.make_move(move,self.opponent[self.color])
        else:
            self.color = 1
        moves = self.board.get_all_possible_moves(self.color)
        index = randint(0,len(moves)-1)
        inner_index =  randint(0,len(moves[index])-1)
        # move = moves[index][inner_index]
        move = self.min_max_recursion(4, True)[0]
        self.board.make_move(move,self.color)
        return move

    def min_max_recursion(self, depth, maximizingPlayer):

        if depth == 0 and self.color == 1:
            return self.board.black_count - self.board.white_count

        elif depth == 0 and self.color == 2:
            return self.board.white_count - self.board.black_count

        maximum = -100
        max_move = ""
        minimum = 100
        min_move = ""
        if maximizingPlayer:
            selfmoves = self.board.get_all_possible_moves(self.color)
            #maximum = -100
            for s_checker_moves in selfmoves:
                for sm in s_checker_moves:
                    self.board.make_move(sm, self.color)
                    Recurs = self.min_max_recursion(depth - 1, False)
                    # print("Recurs: ",Recurs)
                    temp = maximum
                    if type(Recurs) == type(tuple()):
                        maximum = max(maximum, Recurs[1])
                    else:
                        maximum = max(maximum, Recurs)
                    # print("maximum: ",maximum)
                    if temp != maximum:
                        max_move = sm
                    #alpha = max(alpha, Recurs)
                    # print("alpha",alpha)

                    self.board.undo()

                    #if beta <= alpha:
                    #    break
            return (max_move, maximum)

        else:
            #minimum = 100
            oppmoves = self.board.get_all_possible_moves(self.opponent[self.color])
            for o_checker_moves in oppmoves:
                for om in o_checker_moves:
                    self.board.make_move(om, self.opponent[self.color])
                    Recurs = self.min_max_recursion(depth - 1, True)
                    # print("Recurs: ",Recurs)
                    temp = minimum
                    if type(Recurs) == type(tuple()):
                        minimum = min(minimum, Recurs[1])
                    else:
                        minimum = min(minimum, Recurs)
                    # print("minimum: ",minimum)
                    if temp != minimum:
                        min_move = om
                    #beta = min(beta, Recurs)
                    # print("beta: ", beta)

                    self.board.undo()

                    #if beta <= alpha:
                    #    break
            return (min_move, minimum)
Esempio n. 8
0
class StudentAI():
    def __init__(self, col, row, p):
        self.col = col
        self.row = row
        self.p = p
        self.board = Board(col, row, p)
        self.board.initialize_game()
        self.color = ''
        self.opponent = {1: 2, 2: 1}
        self.color = 2
        self.movecount = 1

        self.file = f"{self.col}-{self.row}-{self.color}-{randint(0, 500)}-test.txt"
        self.start = time.time()

        self.theta1, self.theta2 = self.get_theta()
        self.cutoff = self.get_cutoff()

        # self.theta = [8.61043154e+00,  4.48291855e+00,  7.78473553e+00, -7.07767178e-14,2.06230092e+00,  1.18768964e+00]#, 0]
        # self.theta = [-24.13, -7.87, -17.89, -16.67, -6.99, 7.22, 1.19, 0.72,
        #               -4.2, -4.52, -2.49, -3.14, 5.69, 0.02, 3.53, -3.58, 9.37,
        #               -3.81, -1.58, -1.75, 2.51, 0.26, 18.3, 10.25, 3.63,
        #               3.69, 1.32, -4.03]
        # self.theta = [-57.35, -6.41, -2.09, -38.9, -3.91, 6.48, 11.97, -0.39, 27.23, 11.11, -22.04, -11.36, 39.62, -41.32,
        #    55.17, 24.54, 16.05, 12.08, 10.46, -17.8, 5.61, -7.38, 48.46, 20.26, 4.3, 2.54, 0.0, 0.0]
        # self.theta77 = [-1.49, 0.41, 0.0, -0.19, -0.07, 0.25, 0.13, 0.0, 0.0, 0.09, -0.28, -0.53, 3.83, -3.95,
        #     1.88, 0.93, 0.08, 0.25, 0.17, 0.0, -0.22, 0.0, -0.24, -0.2, -0.02, 0.03, 0.0, 0.0]
        # self.theta98 = [-1.76, -0.4, 0.03, -0.08, 0.16, 0.3, 0.16, 0.55, -0.38, -0.17, -0.12, 0.28, 2.8, -2.77,
        #     1.82, 0.82, 0.22, 0.1, 0.09, -0.38, -0.09, -1.31, 0.78, 0.42, -0.02, 0.15, 0.0, 0.0]

    def get_move(self, move):
        print(self.color)

        self.time = time.time()
        # if self.time - self.start > 400:
        #     self.depth = 4
        if len(move) != 0:
            self.board.make_move(move, self.opponent[self.color])
        else:
            self.color = 1
        moves = self.board.get_all_possible_moves(self.color)

        self.depth = self.get_depth()
        # index = randint(0,len(moves)-1)
        # inner_index =  randint(0,len(moves[index])-1)
        # move = moves[index][inner_index]
        # print(moves)
        move = moves[0][0] if len(moves) == 1 and len(
            moves[0]) == 1 else self.minimax_move(moves)
        self.board.make_move(move, self.color)
        self.movecount += 1
        # with open(self.file, 'a') as f:
        #     f.write(f"Movecount:{self.movecount} Total time:{time.time()-self.start} This move takes:{time.time()-self.time} Depth:{self.depth}\n")
        return move

    def get_cutoff(self):
        if self.row == 7 and self.col == 7:
            return (5, 3)
        else:
            return (6, 3)

    def get_depth(self):
        if self.row == 7 and self.col == 7:
            return 0
        else:
            return 4

    def get_theta(self):
        theta771_start = [
            -3.05, -1.9, 0.04, -0.85, -0.06, 0.43, 0.21, 0.0, -0.18, 0.56,
            -0.32, -0.25, 2.9, -2.56, 1.23, 0.06, 0.44, 0.89, 0.67, 0.27, -0.0,
            0.0, 0.64, 0.56, 0.07, 0.91, 0.0, 0.0
        ]
        theta771_mid = [
            -2.84, -1.09, -0.05, -0.67, 0.22, 0.42, 0.24, 0.0, -0.18, 0.72,
            0.26, 0.38, 3.44, -2.88, 1.83, 0.04, 0.3, 0.79, 0.11, -0.02, -0.14,
            0.0, 0.36, 0.3, -0.45, 0.9, 0.0, 0.0
        ]
        theta771_last = [
            -3.01, -1.01, -0.06, 0.13, 0.42, 0.18, 0.03, 0.0, -0.51, 0.75,
            1.31, 1.26, 2.86, -2.55, 1.5, 0.22, 0.05, 0.42, 0.2, -0.06, -0.16,
            0.0, 0.53, 0.45, -0.26, 0.44, 0.0, 0.0
        ]

        theta772_start = [
            2.07, -0.44, 0.29, 0.45, 0.32, -0.46, -0.04, 0.0, 0.37, -0.25,
            0.16, 0.17, 0.0, 0.0, -2.18, -0.62, -0.16, -1.0, 0.24, 0.72, 0.2,
            0.0, 0.12, 0.04, 0.75, -1.29, 3.36, -2.93
        ]
        theta772_mid = [
            1.93, 0.09, 0.16, 0.37, 0.28, -0.18, -0.17, 0.0, 0.02, -0.28, 0.02,
            0.13, 0.0, 0.0, -2.3, -1.12, -0.07, -0.63, 0.38, 0.08, 0.16, 0.0,
            -0.4, 0.43, 0.54, -0.74, 3.47, -3.05
        ]
        theta772_last = [
            1.51, 0.44, -0.01, 0.11, 0.16, -0.16, -0.14, 0.0, -0.17, 0.47,
            -0.16, 0.02, 0.0, 0.0, -2.79, -0.98, 0.0, 0.14, 0.4, -0.08, 0.0,
            0.0, -0.26, 1.25, 0.06, -0.07, 2.32, -2.19
        ]

        theta981_start = [
            -2.85, -0.22, 0.11, -0.54, 0.31, -0.03, 0.07, 0.0, -0.19, 0.45,
            -0.17, 0.45, 2.09, -1.88, 2.46, 1.24, -0.04, 0.64, 0.41, -0.36,
            0.11, 0.25, -0.01, 0.09, -0.34, 0.96, 0.0, 0.0
        ]
        theta981_mid = [
            -1.95, -1.35, 0.11, -0.44, 0.11, 0.16, 0.02, 0.0, -0.18, 0.57,
            0.26, 0.54, 2.37, -2.06, 1.22, -0.19, 0.3, 0.9, 0.43, 0.09, 0.05,
            -0.06, 0.19, 0.19, -0.04, 0.69, 0.0, 0.0
        ]
        theta981_last = [
            -3.22, -1.85, 0.18, 0.48, 0.23, 0.14, -0.18, 0.0, -0.53, 1.25,
            0.93, 0.03, 3.33, -3.01, 2.08, 0.05, 0.15, 0.65, 0.27, 0.03, -0.13,
            -1.0, -0.24, -0.25, 0.01, 0.03, 0.0, 0.0
        ]

        theta982_start = [
            1.47, -0.0, 0.21, 0.5, 0.09, -0.09, 0.05, 0.0, 0.12, -0.45, 0.15,
            0.55, 0.0, 0.0, -2.5, -0.49, 0.05, -0.45, 0.24, 0.24, 0.29, 0.29,
            0.02, 0.12, 0.26, -0.26, 2.31, -2.11
        ]
        theta982_mid = [
            1.22, -0.16, 0.29, 0.22, 0.28, -0.21, -0.05, 0.0, 0.38, -0.24,
            -0.31, 0.59, 0.0, 0.0, -2.04, -1.22, 0.14, -0.54, 0.12, -0.07,
            0.05, -0.04, 0.31, 0.57, 0.41, -0.88, 3.14, -2.84
        ]
        theta982_last = [
            2.12, -0.19, 0.19, -0.23, 0.33, -0.17, -0.11, 0.0, -0.44, 0.32,
            0.16, 0.0, 0.0, 0.0, -2.06, -1.58, 0.09, -0.16, 0.46, -0.19, 0.0,
            -0.94, -0.7, 0.74, 1.33, -0.08, 4.14, -3.98
        ]

        if self.row == 7 and self.col == 7:
            return (theta771_start, theta771_mid,
                    theta771_last), (theta772_start, theta772_mid,
                                     theta772_last)
        else:
            return (theta981_start, theta981_mid,
                    theta981_last), (theta982_start, theta982_mid,
                                     theta982_last)

    def minimax_move(self, moves: [list]):
        best = []
        max_value = -math.inf
        for chess in moves:
            for move in chess:
                val = self.min_value(move, self.depth, -math.inf, math.inf)
                if val > max_value:
                    best = [move]
                    max_value = val
                elif val == max_value:
                    best.append(move)
        return best[0]

    def min_value(self, move, depth, alpha, beta):
        self.board.make_move(move, self.color)

        if depth == 0:
            u = self.utility(self.board, self.color)
            print(u)
            self.board.undo()
            return u

        moves = self.board.get_all_possible_moves(self.opponent[self.color])
        moves = [m for sub in moves for m in sub]
        # moves = self.reorder(self.get_u_list(moves, self.board, self.opponent[self.color]), reverse = False)

        if len(moves) == 0:
            u = +1000
            self.board.undo()
            return u

        min_val = math.inf
        for move in moves:
            min_val = min(self.max_value(move, depth - 1, alpha, beta),
                          min_val)
            beta = min(beta, min_val)
            if alpha >= beta:
                self.board.undo()
                return min_val
        self.board.undo()
        return min_val

    def max_value(self, move, depth, alpha, beta):
        self.board.make_move(move, self.opponent[self.color])

        if depth == 0:
            u = self.utility(self.board, self.opponent[self.color])
            print(u)
            self.board.undo()
            return u

        moves = self.board.get_all_possible_moves(self.color)
        moves = [m for sub in moves for m in sub]
        # moves = self.reorder(self.get_u_list(moves, self.board, self.color), reverse = True)

        if len(moves) == 0:
            u = -1000
            self.board.undo()
            return u

        max_val = -math.inf
        for move in moves:
            max_val = max(self.min_value(move, depth - 1, alpha, beta),
                          max_val)
            alpha = max(alpha, max_val)
            if alpha >= beta:
                self.board.undo()
                return max_val
        self.board.undo()
        return max_val

    def u_after_move(self, move, board, color):
        board.make_move(move, color)
        u = self.utility(board, color)
        board.undo()
        return u

    def get_u_list(self, moves, board, color):
        u_list = {}
        for chess in moves:
            for move in chess:
                u_list[move] = self.u_after_move(move, board, color)
        return u_list

    def reorder(self, u_list, reverse):
        return sorted(u_list, key=lambda x: u_list[x], reverse=reverse)

    def utility(self, board, color):
        wking, bking = self.wking_bking(board)
        wcount, bcount = self.wcount_bcount(board)
        wdis, bdis = self.wdis_bdis(board)
        wedge, bedge = self.wedge_bedge(board)
        wcenter, bcenter = self.wcenter_bcenter(board)
        wback, bback = self.wback_bback(board)

        wdiag, bdiag = self.wdiag_bdiag(board)
        wdog, bdog = self.wdog_bdog(board)
        wbridge, bbridge = self.wbridge_bbridge(board)
        wuptriangle, buptriangle = self.wuptriangle_buptriangle(board)
        wdowntriangle, bdowntriangle = self.wdowntriangle_bdowntriangle(board)
        woreo, boreo = self.woreo_boreo(board)
        board.show_board()
        if color == 1:
            wmoveable, weatable = self.moveables(board, 2)
            bmoveable, beatable = 0, 0
        else:
            wmoveable, weatable = 0, 0
            bmoveable, beatable = self.moveables(board, 1)

        if self.color == 1:
            features = [
                wcount, wking, wdis, wback, wedge, wcenter, wdiag, wdog,
                wbridge, wuptriangle, wdowntriangle, woreo, wmoveable,
                weatable, bcount, bking, bdis, bback, bedge, bcenter, bdiag,
                bdog, bbridge, buptriangle, bdowntriangle, boreo, bmoveable,
                beatable
            ]
            print(str([i for i in features]))
            if bcount > self.cutoff[0]:
                return sum(x * t for x, t in zip(features, self.theta1[0]))
            elif self.cutoff[1] < bcount <= self.cutoff[0]:
                return sum(x * t for x, t in zip(features, self.theta1[1]))
            else:
                return sum(x * t for x, t in zip(features, self.theta1[2]))

        else:
            features = [
                wcount, wking, wdis, wback, wedge, wcenter, wdiag, wdog,
                wbridge, wuptriangle, wdowntriangle, woreo, wmoveable,
                weatable, bcount, bking, bdis, bback, bedge, bcenter, bdiag,
                bdog, bbridge, buptriangle, bdowntriangle, boreo, bmoveable,
                beatable
            ]
            print(str([i for i in features]))
            if wcount > self.cutoff[0]:
                return sum(x * t for x, t in zip(features, self.theta2[0]))
            elif self.cutoff[1] < wcount <= self.cutoff[0]:
                return sum(x * t for x, t in zip(features, self.theta2[1]))
            else:
                return sum(x * t for x, t in zip(features, self.theta2[2]))

    def features(self, board, color):
        '''
        :param board:
        :return: white_features, black_features
            features order = [count, king, dis, back, edge,
                            center, diag, dog, bridge, uptriangle,
                            downtriangle, oreo, moveable, eatable]
        '''
        wfeature = [0 for _ in range(14)]
        bfeature = [0 for _ in range(14)]
        wfeature[0], bfeature[0] = board.white_count, board.black_count

        for r in range(board.row):
            # count edge
            wfeature[4] += (board.board[r][0].color == "W") + (
                board.board[r][board.col - 1].color == "W")
            bfeature[4] += (board.board[r][0].color == "B") + (
                board.board[r][board.col - 1].color == "B")

            for c in range(board.col):
                wfeature[3] += (board.board[board.row - 1][c].color == "B"
                                )  # count back
                bfeature[3] += (board.board[0][c].color == "B")  # count back

                wfeature[5] += (board.board[int(
                    board.row / 2)][c].color == "W") + (
                        board.board[int(board.row / 2) + 1][c].color == "W"
                    )  # count center
                bfeature[5] += (board.board[int(
                    board.row / 2)][c].color == "B") + (
                        board.board[int(board.row / 2) + 1][c].color == "B"
                    )  # count center

                if board.board[r][c].color == 'W':
                    if board.board[r][c].is_king:
                        wfeature[1] += 1  # count king
                    wfeature[2] += board.row - 1 - r  # count dis
                elif board.board[r][c].color == 'B':
                    if board.board[r][c].is_king:
                        bfeature[1] += 1  # count king
                    bfeature[2] += r  # count dis

    def wcount_bcount(self, board):
        return board.white_count, board.black_count

    def wking_bking(self, board):
        bking, wking = 0, 0
        for r in range(self.board.row):
            for c in range(self.board.col):
                if self.board.board[r][c].color == "B":
                    bking += self.board.board[r][c].is_king
                elif self.board.board[r][c].color == "W":
                    wking += self.board.board[r][c].is_king
        return wking, bking

    def moveables(self, board, color):
        moves = [
            m for chess in board.get_all_possible_moves(color) for m in chess
        ]
        eatable = 0
        for m in moves:
            if len(m.seq) > 2:
                eatable += (len(m.seq) - 1)
                continue
            if math.sqrt((m.seq[0][0] - m.seq[1][0])**2 +
                         (m.seq[0][1] - m.seq[1][1])**2) > 1:
                eatable += 1
        # print(f"len(moves): {len(moves)}, eatable: {eatable}")
        return len(moves), eatable

    def wback_bback(self, board):
        bback = sum(board.board[0][i].color == "B" for i in range(board.col))
        wback = sum(board.board[board.row - 1][i].color == "W"
                    for i in range(board.col))
        return wback, bback

    def wedge_bedge(self, board):
        bedge = sum((board.board[i][0].color == "B") +
                    (board.board[i][board.col - 1].color == "B")
                    for i in range(board.row))
        wedge = sum((board.board[i][0].color == "W") +
                    (board.board[i][board.col - 1].color == "W")
                    for i in range(board.row))
        # print(f"wedge: {wedge}, bedge: {bedge}")
        return wedge, bedge

    def wcenter_bcenter(self, board):
        wcenter = sum((board.board[int(board.row / 2)][i].color == "W") + \
                      (board.board[int(board.row / 2) + 1][i].color == "W") for i in range(board.col))
        bcenter = sum((board.board[int(board.row / 2)][i].color == "B") + \
                      (board.board[int(board.row / 2) + 1][i].color == "B") for i in range(board.col))
        # print(f"wcenter: {wcenter}, bcenter: {bcenter}")
        return wcenter, bcenter

    def wdiagonal_bdiagonal(self, board):
        bdiagonal = sum(board.board[i][i].color == "B" for i in range(board.row // 4, 3 * board.row // 4)) + \
                    sum(board.board[board.row - 1 - i][board.row - 1 - i].color == "B" for i in range(board.row))
        wdiagonal = sum(board.board[i][i].color == "W" for i in range(board.row)) + \
                    sum(board.board[board.row - 1 - i][board.row - 1 - i].color == "W" for i in range(board.row))
        # print(f"wdiagonal: {wdiagonal}, bdiagonal: {bdiagonal}")
        return wdiagonal, bdiagonal

    def wdiag_bdiag(self, board):
        bc, wc = 0, 0
        for r in range(board.row - 1):
            bc += (board.board[r][r].color == "B") + (board.board[r + 1][r].color == "B") + (
                    board.board[r][r + 1].color == "B") \
                  + (board.board[r][board.col - 1 - r].color == "B") + (
                          board.board[r + 1][board.col - 1 - r].color == "B") + \
                  (board.board[r][board.col - 2 - r].color == "B")

            wc += (board.board[r][r].color == "W") + (board.board[r + 1][r].color == "W") + (
                    board.board[r][r + 1].color == "W") \
                  + (board.board[r][board.col - 1 - r].color == "W") + (
                          board.board[r + 1][board.col - 1 - r].color == "W") + \
                  (board.board[r][board.col - 2 - r].color == "W")
        bc += (board.board[board.row - 1][0].color == "B") + (
            board.board[board.row - 1][board.row - 1].color == "B")
        wc += (board.board[board.row - 1][0].color == "W") + (
            board.board[board.row - 1][board.row - 1].color == "W")

        # print(f"wdiag: {wc}, bdiag: {bc}")
        return wc, bc

    def wdog_bdog(self, board):
        wc = (board.board[board.row - 1][board.col - 1].color == "." and board.board[board.row - 1][
            board.col - 2].color == "W" \
              and board.board[board.row - 2][board.col - 1].color == "B") + \
             (board.board[board.row - 1][0].color == "." and board.board[board.row - 1][1].color == "W" \
              and board.board[board.row - 2][0].color == "B")

        bc = (board.board[0][0].color == "." and board.board[0][1].color == "B" \
              and board.board[1][0].color == "W") + \
             (board.board[0][board.col - 1].color == "." and board.board[0][board.col - 2].color == "B" \
              and board.board[1][board.col - 1].color == "W")
        # print(f"wdog: {wc}, bdog: {bc}")
        return wc, bc

    def wbridge_bbridge(self, board):
        bc = sum(
            board.board[0][c].color == "B" and board.board[0][c +
                                                              2].color == "B"
            for c in range(1, board.col - 3))
        wc = sum(board.board[board.row - 1][c].color == "W"
                 and board.board[board.row - 1][c + 2].color == "W"
                 for c in range(1, board.col - 3))
        # print(f"wbridge: {wc}, bbridge: {bc}")
        return wc, bc

    def wuptriangle_buptriangle(self, board):
        bcount, wcount = 0, 0
        for r in range(1, board.row - 1):
            for c in range(board.col - 2):
                if board.board[r][c].color == "B" and board.board[r - 1][
                        c + 1].color == "B" and board.board[r][c +
                                                               2].color == "B":
                    bcount += 1
                if board.board[r][c].color == "W" and board.board[r - 1][
                        c + 1].color == "W" and board.board[r][c +
                                                               2].color == "W":
                    wcount += 1
        # print(f"wuptriangle: {wcount}, buptriangle: {bcount}")
        return wcount, bcount

    def wdowntriangle_bdowntriangle(self, board):
        bcount, wcount = 0, 0
        for r in range(board.row - 1):
            for c in range(board.col - 2):
                if board.board[r][c].color == "B" and board.board[r + 1][
                        c + 1].color == "B" and board.board[r][c +
                                                               2].color == "B":
                    bcount += 1
                if board.board[r][c].color == "W" and board.board[r + 1][
                        c + 1].color == "W" and board.board[r][c +
                                                               2].color == "W":
                    wcount += 1
        # print(f"wdowntriangle: {wcount}, bdowntriangle: {bcount}")
        return wcount, bcount

    def woreo_boreo(self, board):
        '''
        :param board:
        :return: triangle pattern in the last row
        '''
        boreo = sum(board.board[0][c].color == "B" and board.board[1][c + 1].color == "B" \
                    and board.board[0][c + 2].color == "B" for c in range(0, board.col - 2))
        woreo = sum(board.board[board.row - 1][c].color == "W" and board.board[board.row - 2][c + 1].color == "W" \
                    and board.board[board.row - 1][c + 2].color == "W" for c in range(0, board.col - 2))
        # print(f"woreo: {woreo}, boreo: {boreo}")
        return woreo, boreo

    def wdis_bdis(self, board):
        wdis = sum(board.row - 1 - i for i in range(board.row)
                   for j in range(board.col) if board.board[i][j].color == "W")
        bdis = sum(i for i in range(board.row) for j in range(board.col)
                   if board.board[i][j].color == "B")
        return wdis, bdis
Esempio n. 9
0
class StudentAI():
    def __init__(self, col, row, p):
        self.col = col
        self.row = row
        self.p = p
        self.board = Board(col, row, p)
        self.board.initialize_game()
        self.color = ''
        self.opponent = {1: 2, 2: 1}
        self.color = 2
        #---------------------------------#
        self.area = self.row * self.col
        self.count = 0
        if self.area <= 39:
            self.depth = 8
        elif self.area <= 49:
            self.depth = 5
        elif self.area <= 79:
            self.depth = 4
        else:
            self.depth = 4

    #get move of current game state
    def get_move(self, move):
        if len(move) != 0:
            self.board.make_move(move, self.opponent[self.color])
        else:
            self.color = 1
        #----------------------------------------------------------#
        # MINIMAX GAME TREE SEARCH AGENT
        #----------------------------------------------------------#
        root = MinimaxTree(self.opponent[self.color])
        self.recursive_dfs(root, self.depth)
        self.recursive_minimax(root)
        move_list = root.data[list(root.data)[0]]
        current = move_list[0]
        #self.board.make_move(current, self.color)
        best_mcts_move = current
        move = current
        #----------------------------------------------------------#
        '''
        #returns some random move from the list
        #--- DEBUGGING PURPOSES ---
        moves_user = self.board.get_all_possible_moves(self.color)
        moves_opponent = self.board.get_all_possible_moves(self.opponent[self.color])
        print("---USER MOVES---")
        for item in moves_user:
            for i in item:
                print(i.seq)
        print("---OPPONENT  MOVES---")
        for item in moves_opponent:
            for i in item:
                print(i.seq)

        board_sim = copy.deepcopy(self.board)
        move_sim = moves_user[0]
        print("Simulation Making move:" + str(move_sim))
        board_sim.make_move(move_sim[0], self.color)
        print("----- SIMULATION B -----")
        board_sim.show_board()
        print("Simulation score =" + str(self.board_heuristic(board_sim)))
        print("Terminal? :" + str(board_sim.is_win(self.color)))
        print("----------------------")

        #current2 = Move(moves_user[randint(0, len(moves_user) - 1)])
        #self.board.make_move(list(current2[0]), self.color)
        #print(current2)
        print("AI Making Move:" + str(move))'''
        #----------------------------------------------------------#
        # MONTE CARLO TREE SEARCH AGENT
        #----------------------------------------------------------#
        m_list = self.board.get_all_possible_moves(self.color)
        if len(m_list) == 1:
            self.board.make_move(m_list[0][0], self.color)
            return m_list[0][0]
        root = MonteCarloTree(self.board, self.color, m_list)
        self.board.make_move(best_mcts_move, self.color)
        move = best_mcts_move
        #----------------------------------------------------------#

        return move

        # -------- CHECKERS BOARD HEURISTIC FUNCTION --------#
        #Sources Referred:
        #https://github.com/techwithtim/Python-Checkers-AI/blob/master/checkers/board.py
        #https://www.cs.huji.ac.il/~ai/projects/old/English-Draughts.pd

    @staticmethod
    def check_distance(p1, p2):
        #for two given checker pieces return the distance
        #using the distance formula sqrt((x2-x1)^2 + (y2-y1)^2)
        dist = sqrt(((p1[0] - p2[0])**2 + (p1[1] - p2[1])**2))
        return dist

    def board_heuristic(self, board):
        score = 0
        king_score = 10 + (self.col - 1)

        pawns_b = []  #black pawns
        pawns_w = []  #white pawns
        kings_b = []  #black kings
        kings_w = []  #white kings
        for r in range(self.row):
            for c in range(self.col):
                piece = board.board[r][c]
                if piece.color == "B":
                    if piece.is_king:
                        kings_b.append((r, c))
                    else:
                        pawns_b.append((r, c))
                elif piece.color == "W":
                    if piece.is_king:
                        kings_w.append((r, c))
                    else:
                        pawns_w.append((r, c))

        for pb in pawns_b:
            score = score + pb[0] + 10
        for pw in pawns_w:
            score = score - (10 + (self.row - 1 - pw[0]))

        for kb in kings_b:
            score = score + king_score
            distance = 0

            for kw in kings_w:
                distance = distance + self.check_distance(kb, kw)
            for pw in pawns_w:
                distance = distance + self.check_distance(kb, pw)
            if len(kings_w) + len(pawns_w) != 0:
                score = score - (distance / (len(kings_w) + len(pawns_w)))

        for kw in kings_w:
            score = score + king_score
            distance = 0

            for kb in kings_b:
                distance = distance + self.check_distance(kw, kb)
            for pb in pawns_b:
                distance = distance + self.check_distance(kw, pb)
            if len(kings_b) + len(pawns_b) != 0:
                score = score - (distance / (len(kings_b) + len(pawns_b)))

        if self.color == 2:
            return score
        else:
            return -score

    #Sources Referred:
    #https://www.geeksforgeeks.org/depth-first-search-or-dfs-for-a-graph/
    def recursive_dfs(self, root: MinimaxTree, depth=1):
        if depth == 0:
            pass
        else:
            if root.move is not None:
                self.board.make_move(root.move, root.color)
            all_moves_list = self.board.get_all_possible_moves(
                self.opponent[root.color])
            for r in range(len(all_moves_list)):
                for c in range(len(all_moves_list[r])):
                    root.child_nodes.append(
                        MinimaxTree(self.opponent[root.color],
                                    all_moves_list[r][c]))
            for node in root.child_nodes:
                self.recursive_dfs(node, depth - 1)
            if root.move is not None:
                self.board.undo()

    #-------- MINIMAX ALGORITHM-------#
    #Sources Referred:
    #https://www.geeksforgeeks.org/minimax-algorithm-in-game-theory-set-1-introduction/?ref=lbp
    #https://www.geeksforgeeks.org/minimax-algorithm-in-game-theory-set-4-alpha-beta-pruning/
    #https://github.com/techwithtim/Python-Checkers-AI/blob/master/minimax/algorithm.py

    def min_or_max(self, color):
        if color == self.color:
            return max
        else:
            return min

    def minimax(self, color, children):
        min_or_max = self.min_or_max(color)
        hash_table = {}
        for child in children:
            for val in child.data.keys():
                hash_table.setdefault(val, []).append(child.move)
        return {min_or_max(hash_table): hash_table[min_or_max(hash_table)]}

    def recursive_minimax(self, root: MinimaxTree):
        if root.move is not None:
            self.board.make_move(root.move, root.color)
        if len(root.child_nodes) == 0:
            root.data = {self.board_heuristic(self.board): []}
        else:
            for node in root.child_nodes:
                self.recursive_minimax(node)
            root.data = self.minimax(root.color, root.child_nodes)

        if root.move is not None:
            self.board.undo()

    # -------- MONTE CARLO TREE SEARCH ALGORITHM-------#
    # Sources Referred:
    # https://www.geeksforgeeks.org/ml-monte-carlo-tree-search-mcts/
    # https://int8.io/monte-carlo-tree-search-beginners-guide/
    # https://www.analyticsvidhya.com/blog/2019/01/monte-carlo-tree-search-introduction-algorithm-deepmind-alphago/

    def simulate(self, board, move):
        #print("SIMULATE")
        #board_sim = copy.deepcopy(board)
        #board_sim.make_move(move, self.color)
        board.make_move(move, self.color)
        score = self.board_heuristic(board)  #board_sim
        return score, board  #,board_sim

    def expansion(self, node: MonteCarloTree):
        #print("EXPANSION")
        #print(node)
        if node.board.is_win(self.color) > 0 or node.board.is_win(
                self.opponent[self.color]) > 0:
            node.expanded = True
            return

        for move_set in node.move_list:
            for move in move_set:
                score, new_board = self.simulate(node.board, move)
                m_list = new_board.get_all_possible_moves(self.color)
                new_mcts_node = MonteCarloTree(new_board, self.color, m_list)
                new_mcts_node.move = move
                new_mcts_node.parent_node = node
                new_mcts_node.board_eval = score
                node.child_nodes.append(new_mcts_node)
                node.board.undo()

        #print("FINAL EXPANSION:")
        #print(node)

    def rollout(self, node: MonteCarloTree, steps=0):
        #print("ROLLOUT")
        #print(node)
        if node.board.is_win(self.color) > 0 or node.board.is_win(
                self.opponent[self.color]) > 0:  #or steps > 1000:
            node.no_of_wins += int(node.board.is_win(self.color))
            node.no_of_steps = steps
            #print("Roll-BACKPROPOGATE")
            self.backpropogate(node)
            return

        while node.board.is_win(self.color) < 1 or node.board.is_win(
                self.opponent[self.color]) < 1:
            self.expansion(node)
            #Recursive Expansion
            children = node.child_nodes
            for child in children:
                if not child.expanded:
                    #print("ROLLOUT RECURSE")
                    self.rollout(child, steps + 1)
                    #print("END ROLLOUT RECURSE")
                    return
                else:
                    pass

    def backpropogate(self, node: MonteCarloTree):
        #print("BACKPROPOGATE")
        #print(node)

        #update UCB1 value too

        if node.parent_node is None:
            #print("reached root node")
            return

        if node.parent_node is not None:
            node.parent_node.ucb1_eval += node.ucb1_eval
            node.parent_node.no_of_wins += node.no_of_wins
            node.parent_node.no_of_steps += node.no_of_steps
            node.board.undo()
            #print("Recurse-BACKPROPOGATE")
            self.backpropogate(node.parent_node)
            #print("End Recurse Backpropogate")

    def monte_carlo_tree_search(self, node: MonteCarloTree):
        #print("MCTS SEARCH START")
        self.expansion(node)
        #print(node)
        for nd in node.child_nodes:  #should be a while loop, and always start from the root
            self.rollout(nd)
            root_wins = node.no_of_wins
            self.ucb1_evaluation(root_wins, nd)
        best_child = self.choose_best_child(node)
        return best_child.move

    def choose_best_child(self, node: MonteCarloTree):
        children = sorted(node.child_nodes,
                          key=attrgetter('ucb1_eval'),
                          reverse=True)
        return children[0]

    def ucb1_evaluation(self, no_of_wins_r, node: MonteCarloTree):
        #print("No. of steps = " + str(node.no_of_steps))
        #print("No. of wins at root = " + str(no_of_wins_r))
        #print("No. of wins at this node = " + str(node.no_of_wins))
        node.ucb1_eval = node.no_of_steps + 2 * math.sqrt(
            (math.log(no_of_wins_r)) / (node.no_of_wins + 1))
        return

    '''
Esempio n. 10
0
class StudentAI():
    def __init__(self, col, row, p):
        self.col = col
        self.row = row
        self.p = p
        self.board = Board(col, row, p)
        self.board.initialize_game()
        self.color = ''
        self.opponent = {1: 2, 2: 1}
        self.color = 2

    def get_move(self, move):
        # make opponents move
        if len(move) != 0:
            self.board.make_move(move, self.opponent[self.color])
        # switch turn
        else:
            self.color = 1
        # find player 1 next move
        moves = self.board.get_all_possible_moves(self.color)
        alpha = -INFINITY
        beta = INFINITY
        result = self.move_ordering(self.color)
        for move in moves:
            for m in move:
                limit = 0
                self.board.make_move(m, self.color)
                val = self.min_value(limit + 1, alpha, beta)
                if val > alpha:
                    alpha = val
                    result = m
                self.board.undo()
        self.board.make_move(result, self.color)
        return result

    def max_value(self, limit, alpha, beta):
        if limit == MINIMAX_DEPTH or self.board.is_win(
                self.color) == self.color:
            return self.heuristic()
        #alpha = -INFINITY               # result = -infinity
        # move = self.move_ordering(self.color)
        # if not move:
        #     return alpha
        # self.board.make_move(move,self.color)
        # v = self.min_value(limit+1,alpha,beta)
        # self.board.undo()
        # if v >= beta:
        #     return INFINITY
        # alpha = max(alpha,v)
        val = -INFINITY
        moves = self.board.get_all_possible_moves(self.color)
        for move in moves:
            for m in move:
                self.board.make_move(m, self.color)
                val = max(val, self.min_value(limit + 1, alpha, beta))
                self.board.undo()
                alpha = max(alpha, val)
                if alpha >= beta:
                    return val
        return val

    def min_value(self, limit, alpha, beta):
        if limit == MINIMAX_DEPTH or self.board.is_win(
                self.color) == self.opponent[self.color]:
            return self.heuristic()
        #beta = INFINITY               # result = infinity
        # move = self.move_ordering(self.opponent[self.color])
        # if not move:
        #     return beta
        # self.board.make_move(move,self.opponent[self.color])
        # v = self.max_value(limit+1,alpha,beta)
        # self.board.undo()
        # if alpha >= v:
        #     return -INFINITY
        # beta = min(beta,v)
        # return beta
        val = INFINITY
        moves = self.board.get_all_possible_moves(self.opponent[self.color])
        for move in moves:
            for m in move:
                self.board.make_move(m, self.opponent[self.color])
                val = min(val, self.max_value(limit + 1, alpha, beta))
                self.board.undo()
                beta = min(beta, val)
                if alpha >= beta:  # pruning - go to next move (?)
                    return val
        return val

    def heuristic(self):
        black = 0
        white = 0
        #if (self.board.black_count+self.board.white_count) > (0.4* self.board.p * self.board.col):  # Decide between early game and mid,end game
        for row in range(self.board.row):
            for col in range(self.board.col):
                if self.board.board[row][col].color == "B":
                    if self.board.board[row][col].is_king:  # King = 10
                        black += self.board.row
                    else:
                        black += (5 + self.board.board[row][col].row -
                                  (0.5 * self.board.row)
                                  )  # reg piece = 5 + rows on oponent side

                elif self.board.board[row][col].color == "W":
                    if self.board.board[row][col].is_king:  # King = 10
                        white += self.board.row
                    else:
                        white += (5 + (self.board.row * 0.5) -
                                  self.board.board[row][col].row
                                  )  # reg piece = 5 + rows on oponent s
        if self.color == 1:
            return black - white
        else:
            return white - black

    def move_ordering(self, player):
        best_move_value = -INFINITY if (player == self.color) else INFINITY
        moves = self.board.get_all_possible_moves(player)
        if moves:
            best_move = moves[0][0]
        else:
            return False
        for move in moves:
            for m in move:
                self.board.make_move(m, player)
                v = self.heuristic()
                if v > best_move_value if (
                        player == self.color) else v < best_move_value:
                    best_move_value = v
                    best_move = m
                self.board.undo()
        return best_move
Esempio n. 11
0
class StudentAI():
    def __init__(self, col, row, p):
        self.col = col
        self.row = row
        self.p = p
        self.board = Board(col, row, p)
        self.board.initialize_game()
        self.color = ''
        self.opponent = {1: 2, 2: 1}
        self.color = 2

    def get_move(self, move):
        if len(move) != 0:
            self.board.make_move(move, self.opponent[
                self.color])  # Run opponent's move for self.board
        else:
            self.color = 1

        root = Tree(self.opponent[self.color])  #Tree root
        self.rec_tree(root, search_depth)  #Set up tree
        self.rec_min_max_heuristic(root)

        avail_moves = root.value[list(root.value)[0]]
        #cur_move = avail_moves[0]
        cur_move = avail_moves[randint(0, len(avail_moves) - 1)]
        #print(avail_moves)

        self.board.make_move(cur_move, self.color)  # Make the optimal move
        move = cur_move
        return move

    def ftu(self, color):  #Function to use (min vs max by color)
        if color == self.color:  # Calculate Min
            return max
        else:  # Calculate Max
            return min

    def min_max(self, children,
                color):  # Returns dict -> {Max/min value: Moves to get here}
        ftu = self.ftu(color)  #Use corresponding min or max depending on color
        value_map = {}
        for child in children:
            for v in child.value.keys():
                value_map.setdefault(v, []).append(
                    child.move
                )  # D: {heuristic value: Move to make to get here}
        # print(value_map)
        return {ftu(value_map): value_map[ftu(value_map)]}

    def board_points(
            self):  # 5 + row number for pawns, 5 + row number + 2 for kings
        '''
        def board_points(self):  # 5 + row number for pawns, 5 + row number + 2 for kings
            king_pts_value = 5 + (
                        self.row - 1) + 2  # 5 pts for piece, self.row -1 pts for pts at end of board, + 1 for being king
            pts = 0
            for i in range(self.row):
                for j in range(self.col):
                    checker = self.board.board[i][j]
                    if checker.color == 'B':  # For black side pieces
                        if checker.is_king:
                            pts += king_pts_value
                        else:
                            pts += 5 + checker.row
                    elif checker.color == 'W':  # FOr white side pieces
                        # pts -= (11 - checker.row)  # 5 + (6 - Row)
                        if checker.is_king:
                            pts -= king_pts_value
                        else:
                            pts -= (5 + (
                                        self.row - checker.row - 1))  # 5 + (Num of rows - Row - 1) eg. 5x5 board, 5th row is 5(num) - 4(row) -1 = 0

            if abs(pts) > 2:
                self.dif_val = True
            # if debug: print(color(root.color), pts, -pts)
            return pts if self.color == 1 else -pts  # BLACK(1) GOES FIRST, so positive points, if self.color == white(2), then return white pieces as positive points
            '''
        pts = 0
        for i in range(self.row):
            for j in range(self.col):
                checker = self.board.board[i][j]
                if checker.color == 'B':  # For black side pieces
                    pts += 5 + checker.row
                    if checker.is_king:  # 2 additional pts for king
                        pts += 2
                elif checker.color == 'W':  # FOr white side pieces
                    pts -= 11 - checker.row  # 5 + (6 - Row)
                    if checker.is_king:  # 2 additional pts for king
                        pts -= 2
        return pts if self.color == 1 else -pts

    def print_tree(self, root, level=0):
        # print("PRINTING TREE")

        print("\t" * level, root.value, "->", root.move)
        if len(root.children) != 0:  # Not Leaf node
            for child in root.children:
                self.print_tree(child, level + 1)

    def rec_tree(self, root: Tree, level=1):  #Create tree up to depth level
        if level == 0:
            pass
        else:
            if root.move is not None:  # Not root of tree
                self.board.make_move(root.move, root.color)
            #Check if win here maybe?
            avail_moves = self.board.get_all_possible_moves(
                self.opponent[root.color])
            for i in range(len(avail_moves)):
                for j in range(len(avail_moves[i])):
                    #print(root)
                    root.children.append(
                        Tree(self.opponent[root.color], avail_moves[i][j]))
            for child in root.children:
                self.rec_tree(child, level - 1)

            if root.move is not None:
                self.board.undo()

    def rec_min_max_heuristic(self,
                              root: Tree):  #Apply min_max heuristic to tree
        if root.move is not None:  #If not root of tree, make the move required to get here
            self.board.make_move(root.move, root.color)
        if len(root.children) == 0:  #Passed node has no children
            pass  #Evaluate heuristic for board(and return?)
            root.value = {self.board_points(): []}
        else:  #Evaluate rec_heuristic for children, then retrieve values and apply min/max as appropriate
            for child in root.children:
                self.rec_min_max_heuristic(child)
            root.value = self.min_max(root.children, root.color)

        if root.move is not None:
            self.board.undo()
Esempio n. 12
0
class StudentAI():
    def __init__(self, col, row, p):
        self.col = col
        self.row = row
        self.p = p
        self.board = Board(col, row, p)
        self.board.initialize_game()
        self.color = ''
        self.opponent = {1: 2, 2: 1}
        self.color = 2
        self.f = open("debug.txt", "w")

    def heuristic_black(self):
        count_b = 0
        count_w = 0
        for i in range(int(len(self.board.board) / 2)):
            for j in range(len(self.board.board[i])):
                if self.board.board[i][j].color == 1:
                    if self.board.board[i][j].is_king == True:
                        count_b += 10
                    else:
                        count_b += 5
                else:
                    if self.board.board[i][j].is_king == True:
                        count_w += 10
                    else:
                        count_w += 7
        for i in range(int(len(self.board.board) / 2), len(self.board.board)):
            for j in range(len(self.board.board[i])):
                if self.board.board[i][j].color == 1:
                    if self.board.board[i][j].is_king == True:
                        count_b += 10
                    else:
                        count_b += 7
                else:
                    if self.board.board[i][j].is_king == True:
                        count_w += 10
                    else:
                        count_w += 5

        # for i in self.board.board:
        #     for j in i:
        #
        #         if j.color == 1:
        #             if j.is_king == True:
        #                 count_b += 7 + self.row
        #             else:
        #                 count_b += 5 + (self.row - j.row)
        #         elif j.color == 2:
        #             if j.is_king == True:
        #                 count_w += 7 + self.row
        #             else:
        #                 count_w += 5 + j.row
        return count_b - count_w

    def get_move(self, move):
        if len(move) != 0:
            self.board.make_move(move, self.opponent[self.color])
        else:
            self.color = 1
        moves = self.board.get_all_possible_moves(self.color)
        self.f.write("Curr Moves: " + str(moves) + '\n')
        if len(moves) == 1 and len(moves[0]) == 1:
            move = moves[0][0]
            self.board.make_move(move, self.color)
            return move
        move = self.minimax(moves)
        self.f.write("Chosen Move: " + str(move) + '\n')
        # index = randint(0,len(moves)-1)
        # inner_index =  randint(0,len(moves[index])-1)
        # move = moves[index][inner_index]
        self.board.make_move(move, self.color)
        return move

    def minimax(self, moves):
        dic_l1 = dict()
        for peice in range(len(moves)):
            for i in range(len(moves[peice])):
                move = moves[peice][i]
                self.board.make_move(move, self.color)

                if self.board.is_win(self.color) == self.color:
                    self.board.undo()
                    return moves[peice][i]

                l2_moves = self.board.get_all_possible_moves(
                    self.opponent[self.color])
                # print("Opponent Moves: \n peice: ",peice, "\n dir: ",i, "\nMoves\n", l2_moves)
                dic_l2 = dict()
                for opp_peice in range(len(l2_moves)):
                    for j in range(len(l2_moves[opp_peice])):
                        move = l2_moves[opp_peice][j]
                        self.board.make_move(move, self.opponent[self.color])
                        l3_moves = self.board.get_all_possible_moves(
                            self.color)
                        dic_l3 = dict()
                        # print("L3 ",l3_moves)
                        for my_peice in range(len(l3_moves)):
                            flag = 0
                            for k in range(len(l3_moves[my_peice])):
                                move = l3_moves[my_peice][k]
                                self.board.make_move(move, self.color)
                                value = -1
                                if self.color == 1:
                                    value = (self.board.black_count /
                                             (self.board.black_count +
                                              self.board.white_count)) * 100
                                else:
                                    value = (self.board.white_count /
                                             (self.board.black_count +
                                              self.board.white_count)) * 100

                                key = str(my_peice) + ' ' + str(k)
                                # print(key, ' ', value)
                                dic_l3[key] = value
                                self.board.undo()
                                if self.board.is_win(self.color) == self.color:
                                    flag = 1
                                    break
                            if flag == 1:
                                break

                        if len(dic_l3) == 0:
                            key = str(opp_peice) + ' ' + str(j)
                            dic_l2[key] = int(0x40000)
                            self.board.undo()
                        else:
                            inverse = [(value, key)
                                       for key, value in dic_l3.items()]
                            l2_value = max(inverse)[0]
                            key = str(opp_peice) + ' ' + str(j)
                            dic_l2[key] = l2_value
                            self.board.undo()
                if len(dic_l2) == 0:
                    key = str(peice) + ' ' + str(i)
                    dic_l1[key] = int(-0x40000)
                    self.board.undo()
                else:
                    inverse = [(value, key) for key, value in dic_l2.items()]
                    l1_value = min(inverse)[0]
                    key = str(peice) + ' ' + str(i)
                    dic_l1[key] = l1_value
                    self.board.undo()

        inverse = [(value, key) for key, value in dic_l1.items()]
        l0_value = max(inverse)[1]
        # print(dic_l1)
        # print(l0_value)
        x, y = l0_value.split(' ')
        return moves[int(x)][int(y)]
class StudentAI():

    def __init__(self,col,row,p):
        self.col = col
        self.row = row
        self.p = p
        self.board = Board(col,row,p)
        self.board.initialize_game()
        self.color = ''
        self.opponent = {1:2,2:1}
        self.color = 2

        # new params
        '''
            new data clearification:
                feature_matrix = [[X1, X2, ..., X_feature_size, Y],
                                    ...
                                    [X1m, X2m, ..., X_feature_size_m, Y]]
        '''
        self.movecount = 0
        self.feature_size = 5
        self.thetas = np.random.rand(self.feature_size)
        self.feature_matrix = np.empty((0, self.feature_size))


    def get_move(self, move):
        #print(self.color)
        if len(move) != 0:
            self.board.make_move(move,self.opponent[self.color])
        else:
            self.color = 1
        moves = self.board.get_all_possible_moves(self.color)

    def make_action(self,state):
        # TODO: implement this to return action with max Q value
        for a in get_all_action(state):
            return action with max(self.Q_table[state, action])


        #self.train()
        self.simulate_lr(self.color)

        #index = randint(0,len(moves)-1)
        #inner_index =  randint(0,len(moves[index])-1)
        #move = moves[index][inner_index]
        #print(moves)
        move = self.minimax_move(moves)
        #move = self.monte_carlo_tree([m for chess in moves for m in chess], 10, 10)
        self.board.make_move(move, self.color)
        self.movecount += 1
        return move

    def minimax_move(self, moves: [list]) :
        best = []
        max_value = - math.inf
        depth = 4
        for chess in moves:
            for move in chess:
                val = self.max_value(move, depth, -math.inf, math.inf)
                if val > max_value:
                    best = [move]
                    max_value = val
                elif val == max_value:
                    best.append(move)
        if len(best) == 1:
            return best[0]
        # print(len(best))
        # best[randint(0,len(best)-1)]
        return self.monte_carlo_tree(best, 10, 10)

    def explore(self, state):
        epsilon = 0.2
        if random.uniform(0,1) < epsilon:
            # TODO : implement select random move
        else:
            # TODO: implement explore max reward

    def monte_carlo_tree(self, moves: [], simulate_times: int, s_parent: int):
        best_uct = 0
        best_move = 0
        for move in moves:
            wins = self.simulate(move, simulate_times)
            uct = wins/simulate_times + math.sqrt(2*math.log(s_parent)/simulate_times)
            if uct > best_uct:
                best_move = move
        return best_move

    def simulate(self, move, s):
        wins = 0
        board = copy.deepcopy(self.board)
        board.make_move(move, self.color)
        for i in range(s):
            curr_turn = self.opponent[self.color]
            t = 0
            for turn in range(20):
                if board.is_win(self.color) == self.color:
                    wins = 1
                    self.undo(board,t)
                    break
                elif board.is_win(self.opponent[self.color]) == self.opponent[self.color]:
                    self.undo(board, t)
                    break
                moves = board.get_all_possible_moves(curr_turn)
                index = randint(0,len(moves)-1)
                inner_index =  randint(0,len(moves[index])-1)
                board.make_move(moves[index][inner_index], curr_turn)
                curr_turn = self.opponent[curr_turn]
                t += 1
            else:
                wins = 0.5
                self.undo(board, t)

        return wins


    def undo(self, board, times):
        for _ in range(times):
            board.undo()

    def train_one_episode(self):
        # This is training function for one episode, start a new board and
        # update Q value until win or loss
        # QUESTION IS HOW TO DECIDE OPPONENT MOVE? BY SELF-TRAIN? AND HOW TO SELF-TRAIN?
        new_board = Board()
        new_board.initialize_game()
        turn = ''


    def min_value(self, move, depth, alpha, beta):
        self.board.make_move(move, self.opponent[self.color])
        if depth == 0:
            #u = self.utility(self.board)
            u = self.utility_with_theta(self.board)
            self.board.undo()
            return u
        min_val = math.inf
        for chess in self.board.get_all_possible_moves(self.color):
            for move in chess:
                min_val = min(self.max_value(move, depth - 1, alpha, beta), min_val)
                beta = min(beta, min_val)
                if alpha >= beta:
                    self.board.undo()
                    return min_val
        self.board.undo()
        return min_val

    def max_value(self, move, depth, alpha, beta):
        self.board.make_move(move, self.color)
        if depth == 0:
            #u = self.utility(self.board)
            u = self.utility_with_theta(self.board)
            self.board.undo()
            return u
        max_val = - math.inf
        for chess in self.board.get_all_possible_moves(self.opponent[self.color]):
            for move in chess:
                max_val = max(self.min_value(move, depth - 1, alpha, beta), max_val)
                alpha = max(alpha, max_val)
                if alpha >= beta:
                    self.board.undo()
                    return max_val
        self.board.undo()
        return max_val

    def utility(self, board):
        bking, wking = 0, 0
        for r in range(self.board.row):
            for c in range(self.board.col):
                if self.board.board[r][c].color == "B":
                    bking += self.board.board[r][c].is_king
                elif self.board.board[r][c].color == "W":
                    wking += self.board.board[r][c].is_king

        bback = sum(self.board.board[0][i].color == "B" for i in range(self.board.col))
        wback = sum(self.board.board[self.board.row-1][i].color == "W" for i in range(self.board.col))

        bedge = sum(self.board.board[i][0].color == "B"+self.board.board[i][self.board.col-1].color == "B" for i in range(self.board.row))
        wedge = sum(self.board.board[i][0].color == "W"+self.board.board[i][self.board.col-1].color == "W" for i in range(self.board.row))

        time_param = math.log(self.movecount)
        b = 3*self.board.black_count + bking * time_param + bback * (1/time_param) + bedge * (1/time_param)
        w = 3*self.board.white_count + wking * time_param + wback * (1/time_param) + wedge * (1/time_param)
        # b = self.board.black_count
        # w = self.board.white_count
        #print("black:",b," white:",w)
        return b-w if self.color == 1 else w-b


    ####################################################
    ### Training heuristics using Linear Regression ####
    ####################################################

    def utility_with_theta(self, board):
        X_black, X_white = self.get_X(board)
        b = X_black.dot(self.thetas)
        w = X_white.dot(self.thetas)

        return b - w if self.color == 1 else w - b


    def get_X(self, board):
        bking, wking = 0, 0
        for r in range(self.board.row):
            for c in range(self.board.col):
                if self.board.board[r][c].color == "B":
                    bking += self.board.board[r][c].is_king
                elif self.board.board[r][c].color == "W":
                    wking += self.board.board[r][c].is_king

        bback = sum(self.board.board[0][i].color == "B" for i in range(self.board.col))
        wback = sum(self.board.board[self.board.row-1][i].color == "W" for i in range(self.board.col))

        bedge = sum(self.board.board[i][0].color == "B"+self.board.board[i][self.board.col-1].color == "B" for i in range(self.board.row))
        wedge = sum(self.board.board[i][0].color == "W"+self.board.board[i][self.board.col-1].color == "W" for i in range(self.board.row))

        X_black = np.array([self.board.black_count, bking, bback, bedge, self.movecount])
        X_white = np.array([self.board.white_count, wking, wback, wedge, self.movecount])

        return X_black, X_white

    # def model(self, X, thetas):
    #     # X = [count, king, back, edge, time]
    #     # thetas = [t1, t2, t3, t4, t5]
    #     #return thetas[0] * X[0] + thetas[1] * X[1] + thetas[2] * X[2] + thetas[3] * X[3] + thetas[4] * X[4]
    #     return X*thetas


    def train(self):
        # TODO: Training thetas by linear regression and mean square error gradient descent
        self.thetas = np.random.rand(5)

        epoch = 0.01
        alpha = 0.05

        while True:
            thetas = thetas - self.Gradient() * alpha
            if self.Gradient() * alpha < epoch:
                break

        return thetas

    def simulate_times(self, color, simulate_times):
        wins = 0
        for _ in simulate_times:
            wins += self.simulate_lr(color)

        return wins

    def simulate_lr(self, color):
        # simulate one time
        # record all X features to feature_matrix
        # update the y value accordingly

        print("entering simulations")
        newboard = Board(self.col, self.row, self.p)
        newboard.initialize_game()

        feature_list_b = []
        feature_list_w = []

        win = 0
        ### TODO: Fixing Current move in a new board
        curr_turn = self.opponent[color]

        for turn in range(50):
            if newboard.is_win(color) == color:
                win = 1
                break
            elif newboard.is_win(self.opponent[color]) == self.opponent[color]:
                break
            move = self.minimax_move(newboard.get_all_possible_moves(curr_turn))
            newboard.make_move(move, curr_turn)

            b, w = self.get_X(self.board)
            feature_list_b.append(b)
            feature_list_w.append(w)

            self.feature_matrix = np.append(self.feature_matrix, np.array([b, w]), axis=0)
            print(self.feature_matrix)
            curr_turn = self.opponent[curr_turn]

        else:
            win = 0.5

        # matrix = np.array([feature_list_b, feature_list_w])
        # feature_matrix = np.hstack((matrix, np.zeros((matrix.shape[0], 1))))

        # TODO: Fixing y value update
        if win == 1 and color == 1:
            for fb in feature_list_b:
                index = np.where(fb in self.feature_matrix[:, 0:self.feature_size])
                if index == []:
                    self.feature_matrix = np.append(self.feature_matrix, np.array([b, w]), axis=0)
                self.feature_matrix[index, self.feature_size] += 1

        elif win == 0 and color == 1:
            for fw in feature_list_w:
                index = np.where(fw in self.feature_matrix[:, 0:self.feature_size])
                if index == []:
                    self.feature_matrix = np.append(self.feature_matrix, np.array([b, w]), axis=0)
                self.feature_matrix[index, self.feature_size] += 1

        return win

    def MSE(self, thetas):
        # TODO: get mean square error by simulation
        wins = self.simulate_times(self.color, 20)

        utility = self.model(self.get_X(self.board), thetas)



    def Gradient(self, mse):
        # TODO: calculate gradient according to mse
        pass

    def move_by_qtable(self):
        # TODO: maybe write a function to choose move from feature matrix accordingly
        pass
Esempio n. 14
0
class StudentAI():
    def __init__(self, col, row, p):
        self.col = col
        self.row = row
        self.p = p
        self.board = Board(col, row, p)
        self.board.initialize_game()
        self.color = ''
        self.opponent = {1: 2, 2: 1}
        self.color = 2

    def get_move(self, move):
        if len(move) != 0:
            self.board.make_move(move, self.opponent[self.color])
        else:
            self.color = 1
        new_move = self.MiniMax()
        if len(new_move.seq) == 0:
            random_moves = self.board.get_all_possible_moves(self.color)
            index = randint(0, len(random_moves) - 1)
            inner_index = randint(0, len(random_moves[index]) - 1)
            random_move = random_moves[index][inner_index]
            self.board.make_move(random_move, self.color)
            return random_move
        else:
            self.board.make_move(new_move, self.color)
            return new_move

    def get_value(self, input_color):
        # add more condition
        value = 0
        count = 0
        if input_color == 1:
            color = 'B'
            for row in range(0, self.row):
                for col in range(0, self.col):
                    checker = self.board.board[row][col]
                    if checker.color == color:
                        # more weight to cornor
                        if (checker.col == 0 and checker.row == self.row - 1):
                            value += 3
                        if (checker.col == self.col - 1
                                and checker.row == self.row - 1):
                            value += 3
                        if checker.is_king:
                            # not addding row num
                            # so that moving forward and backward is equal
                            value += 10
                        else:
                            value += checker.row
                            # give more weight
                            # encourage pawns to become king
                            if checker.row + 1 > self.row / 2:
                                value += (checker.row + 1 - self.row / 2) * 2
                        if row == self.row - 1:
                            value += 5
                        elif col == self.col - 1 or row * col == 0:
                            value += 2
                    elif checker.color != color and checker.color != '.':
                        if checker.is_king:
                            value -= 2 * (checker.row)
                            # value -= 10
                        else:
                            value -= 0.5 * (checker.row)
                        if row == self.row - 1 or col == self.col - 1 or row * col == 0:
                            value -= 2
        else:
            color = 'W'
            for row in range(0, self.row):
                for col in range(0, self.col):
                    checker = self.board.board[row][col]
                    if checker.color == color:
                        # more weight to corner
                        if (checker.col == 0 and checker.row == 0):
                            value += 3
                        if (checker.col == self.col - 1 and checker.row == 0):
                            value += 3
                        if checker.is_king:
                            value += 10
                        else:
                            abs_row = 8 - checker.row
                            value += abs_row
                            if abs_row > self.row / 2:
                                value += (abs_row - self.row / 2) * 2
                        if row == 0:
                            value += 5
                        elif col == self.col - 1 or row * col == 0:
                            value += 2
                    # handle Tie
                    elif checker.color != color and checker.color != '.':
                        if checker.is_king:
                            value -= 2 * (checker.row)
                            # value -= 10
                        else:
                            value -= 0.5 * (checker.row)
                        if row == self.row - 1 or col == self.col - 1 or row * col == 0:
                            value -= 2
        return value

    def Cutoff(self, depth):
        return depth >= 4

    # B -> Max
    def get_MaxVal(self, depth, alpha, beta):
        color = self.color
        if self.Cutoff(depth):
            return self.get_value(self.color)
        val = -inf
        next_moves = self.board.get_all_possible_moves(color)
        for checker_index in range(0, len(next_moves)):
            for move_index in range(0, len(next_moves[checker_index])):
                next_move = next_moves[checker_index][move_index]
                self.board.make_move(next_move, color)
                val = max(val, self.get_MinVal(depth + 1, alpha, beta))
                if val >= beta:
                    self.board.undo()
                    return val
                alpha = max(alpha, val)
                self.board.undo()
        return val

    def get_MinVal(self, depth, alpha, beta):
        color = self.opponent[self.color]
        if self.Cutoff(depth):
            return self.get_value(self.color)
        val = inf
        next_moves = self.board.get_all_possible_moves(color)
        for checker_index in range(0, len(next_moves)):
            for move_index in range(0, len(next_moves[checker_index])):
                next_move = next_moves[checker_index][move_index]
                self.board.make_move(next_move, color)
                val = min(val, self.get_MaxVal(depth + 1, alpha, beta))
                if val <= alpha:
                    self.board.undo()
                    return val
                beta = min(beta, val)
                self.board.undo()
        return val

    def MiniMax(self):
        alpha = -inf
        beta = inf
        best_move = Move([])
        moves = self.board.get_all_possible_moves(self.color)
        for checker_index in range(0, len(moves)):
            for move_index in range(0, len(moves[checker_index])):
                move = moves[checker_index][move_index]
                if len(move) == 0:
                    pass
                depth = 0
                # *(1) start: undo later
                self.board.make_move(move, self.color)
                value = self.get_MinVal(depth + 1, alpha, beta)
                if value > alpha:
                    alpha = value
                    best_move = move
                # *(1) end
                self.board.undo()
        return best_move
Esempio n. 15
0
class StudentAI():
    def __init__(self, col, row, p):
        self.col = col
        self.row = row
        self.p = p
        self.board = Board(col, row, p)
        self.board.initialize_game()
        self.color = ''
        self.opponent = {1: 2, 2: 1}
        self.color = 2
        self.ct = 0
        #self.dif_val = False
        self.size = self.col * self.row
        if self.size < 40:  #6x6
            #print(8)
            self.search_depth = 8
        elif self.size < 50:  #7x7
            #print(7)
            self.search_depth = 5
        elif self.size < 80:  #8x8
            #print(6)
            self.search_depth = 4
        else:
            self.search_depth = 4

    def get_move(self, move):
        if len(move) != 0:
            self.board.make_move(move, self.opponent[
                self.color])  # Run opponent's move for self.board
        else:
            self.color = 1

        try:
            self.search_depth
        except NameError:
            print("ERROR")
            search_depth = 5

        if self.size < 40:  #6x6
            if self.ct == 5:
                self.search_depth += 1  #9
            elif self.ct == 10:
                self.search_depth += 1  #10
        elif self.size < 50:  #7x7
            if self.ct == 2:
                self.search_depth += 1  #6
            elif self.ct == 5:
                self.search_depth += 1  #7
            if self.ct == 10:
                self.search_depth += 1  #8
            elif self.ct == 15:
                self.search_depth += 1  #9
            elif self.ct == 20:
                self.search_depth += 1  #10
        elif self.size < 80:  #8x8
            if self.ct == 3:
                self.search_depth += 1  #5
            elif self.ct == 5:
                self.search_depth += 1  #6
            elif self.ct == 7:
                self.search_depth += 1  #7
            elif self.ct == 11:
                self.search_depth += 1  #8
        else:
            if self.ct == 10:
                self.search_depth += 1
            elif self.ct == 20:
                self.search_depth += 2

        root = Tree(self.opponent[self.color])  # Tree root
        #print('Detph', self.search_depth, self.ct)
        self.rec_tree(root, self.search_depth)  # Set up tree

        self.rec_min_max_heuristic(root)

        #self.rec_abp_heuristic(root)

        #self.rec_abp_v2(root)

        avail_moves = root.value[list(root.value)[0]]

        #cur_move = avail_moves[randint(0,len(avail_moves)-1)]
        cur_move = avail_moves[0]
        '''
        print("ALL MOVES")
        moves = self.board.get_all_possible_moves(self.color)
        for i, checker_moves in enumerate(moves):
            print(i, ':[', end="")
            for j, move in enumerate(checker_moves):
                print(j, ":", move, end=", ")
            print("]")
        print("AVAIL MOVES")
        #print(avail_moves)
        for i, checker_moves in enumerate(avail_moves):
            print(i, ':[', end="")
            for j, move in enumerate(checker_moves):
                print(j, ":", move, end=", ")
            print("]")
        '''
        #if self.dif_val:
        if debug: print("##########TREE##########")
        self.print_tree(root)
        if debug: print("##########TREE##########")
        #            self.dif_val = False
        self.board.make_move(cur_move, self.color)  # Make the optimal move
        move = cur_move
        return move

    # Board Heuristic
    def board_points(
            self):  # 5 + row number for pawns, 5 + row number + 2 for kings
        king_pts_value = 5 + (
            self.row - 1
        ) + 5  #5 pts for piece, self.row -1 pts for pts at end of board, + 1 for being king

        pts = 0
        b_pawns = set()
        b_kings = set()
        w_pawns = set()
        w_kings = set()
        for i in range(self.row):
            for j in range(self.col):
                checker = self.board.board[i][j]
                if checker.color == "B":  #Black
                    if checker.is_king:
                        b_kings.add((i, j))
                    else:
                        b_pawns.add((i, j))
                elif checker.color == "W":  #White
                    if checker.is_king:
                        w_kings.add((i, j))
                    else:
                        w_pawns.add((i, j))
        # if b_pawns == set():
        #     print("-" * 20)
        #     self.board.show_board()
        # b_pawns = set()
        # b_kings = set()
        # w_pawns = set()
        # w_kings = set()
        # for i in range(self.row):
        #     for j in range(self.col):
        #         checker = self.board.board[i][j]
        #         if checker.color == "B": #Black
        #             if checker.is_king:
        #                 b_kings.add((i,j))
        #             else:
        #                 b_pawns.add((i,j))
        #         elif checker.color == "W": #White
        #             if checker.is_king:
        #                 w_kings.add((i,j))
        #             else:
        #                 w_pawns.add((i,j))

        for pawn in b_pawns:
            pts += 5 + pawn[0]
        for pawn in w_pawns:
            pts -= (5 + (self.row - pawn[0] - 1))
        for king in b_kings:
            pts += king_pts_value
            dist = 0
            for w in w_kings:
                dist += sqrt((king[0] - w[0])**2 + (king[1] - w[1])**2)
            for w in w_pawns:
                dist += sqrt((king[0] - w[0])**2 + (king[1] - w[1])**2)
            if len(w_kings) + len(w_pawns) != 0:
                pts -= dist / (len(w_kings) + len(w_pawns))
        for king in w_kings:
            pts -= king_pts_value
            dist = 0
            for b in b_kings:
                dist += sqrt((king[0] - b[0])**2 + (king[1] - b[1])**2)
            for b in b_pawns:
                dist += sqrt((king[0] - b[0])**2 + (king[1] - b[1])**2)
            if len(b_kings) + len(b_pawns) != 0:
                pts += dist / (len(b_kings) + len(b_pawns))

        #if abs(pts) > 2:
#            self.dif_val = True
#if debug: print(color(root.color), pts, -pts)
        return pts if self.color == 2 else -pts  #BLACK(1) GOES FIRST, so positive points, if self.color == white(2), then return white pieces as positive points

    def print_tree(self, root, level=0):
        if not debug:
            return
        print("\t" * level, color(root.color), root.value, "->", root.move)
        if len(root.children) != 0:  # Not Leaf node
            for child in root.children:
                self.print_tree(child, level + 1)

    def rec_tree(self, root: Tree, level=1):  # Create tree up to depth level
        if level == 0:
            pass
        else:
            if root.move is not None:  # Not root of tree
                self.board.make_move(root.move, root.color)
            # Check if win here maybe?
            avail_moves = self.board.get_all_possible_moves(
                self.opponent[root.color])
            for i in range(len(avail_moves)):
                for j in range(len(avail_moves[i])):
                    # print(root)
                    root.children.append(
                        Tree(self.opponent[root.color], avail_moves[i][j]))
            for child in root.children:
                self.rec_tree(child, level - 1)

            if root.move is not None:
                self.board.undo()

    # MinMax Functions
    def ftu(self, color):  # Function to use (min vs max by color)
        if color == self.color:  # Calculate Max
            return max
        else:  # Calculate Min
            return min

    def min_max(self, children,
                color):  # Returns dict -> {Max/min value: Moves to get here}
        ftu = self.ftu(
            color)  # Use corresponding min or max depending on color
        value_map = {}
        for child in children:
            for v in child.value.keys():
                value_map.setdefault(v, []).append(
                    child.move
                )  # D: {heuristic value: Move to make to get here}
        # print(value_map)
        return {ftu(value_map): value_map[ftu(value_map)]}

    def rec_min_max_heuristic(self,
                              root: Tree):  # Apply min_max heuristic to tree
        if root.move is not None:  # AKA this is root, the move is what opponent made to get here (none so we don't have to redo move on our board)
            self.board.make_move(root.move, root.color)
        if len(root.children) == 0:  # Passed node has no children
            # Evaluate heuristic for board(and return?)
            root.value = {
                self.board_points(): []
            }  # Value will be dict with key = heuristic points and value = all the moves that result in that many points
        else:  # Evaluate rec_heuristic for children, then retrieve values and apply min/max as appropriate
            for child in root.children:
                self.rec_min_max_heuristic(child)
            root.value = self.min_max(root.children, root.color)

        if root.move is not None:
            self.board.undo(
            )  # Undo move to revert action (done for searching) and return to parent

    # AlphaBeta Functions
    def set_alpha_beta(self, root, child, color):
        ftu = self.ftu(color)
        if child.value is None:
            print(child)
        if root.value is None:
            root.value = {}
        if color == self.color:  # Max aka update alpha (This ai's turn)
            # return ftu(alpha, ftu(child.value)), beta
            if root.alpha < ftu(child.value):
                root.alpha = ftu(child.value)
            root.value.setdefault(root.alpha, []).append(child.move)
        else:  # Min aka update beta (Opponent's turn)
            # return alpha, ftu(beta, ftu(child.value))
            if root.beta > ftu(child.value):
                root.beta = ftu(child.value)
            root.value.setdefault(root.beta, []).append(child.move)

    def rec_abp_heuristic(self,
                          root: Tree,
                          alpha=-999,
                          beta=999,
                          level=0):  # Alpha Beta Pruning
        if debug:
            print("\t" * level, color(root.color), "Enter: ", root.value, "->",
                  root.move)
        old_val = root.value
        if root.move is not None:  # AKA this is root, the move is what opponent made to get here (none so we don't have to redo move on our board)
            self.board.make_move(root.move, root.color)
        #self.board.show_board()
        if len(
                root.children
        ) == 0:  # Passed node has no children aka this is lowest level/leaf
            root.value = {self.board_points(): []}
            if debug:
                print("\t" * level, "LEAF: ", root.value, "->", root.move)
        else:  # Evaluate heuristic for child, retrieve value, update alphabeta, continue with next child if appropriate
            root.alpha = alpha
            root.beta = beta

            if debug: print("\t" * 16, "CHILDREN:", end=" ")
            for child in root.children:
                if debug: print(child.move, end=", ")
            if debug: print("(", color(self.opponent[root.color]), ")", sep="")

            for child in root.children:
                if root.alpha >= root.beta:  # Break out of loop once alpha >= beta (Pruning)
                    if debug: print("PRUNING")
                    break
                self.rec_abp_heuristic(child, root.alpha, root.beta, level + 1)
                self.set_alpha_beta(
                    root, child, root.color
                )  # Apply alpha/beta values based on min/max of child to current node
                if debug:
                    print("\t" * level, color(root.color), "New Value: ",
                          root.value, "->", root.move)
        if root.move is not None:
            self.board.undo()
        if debug:
            print("\t" * level, color(root.color), "Exit: ", root.value, "->",
                  root.move)
        #print(max(list(root.value), key = abs), "\t", root.move, "->", root.value)
        #if abs(max(list(root.value), key = abs)) > 2:
        #print("\t" * level, "Enter: ", old_val, "->", root.move)
        #print("\t" * level, "Exit: ", root.value, "->", root.move)

    def rec_abp_v2(self, root: Tree, alpha=-999, beta=999):
        if root.move is not None:  # AKA this is root, the move is what opponent made to get here (none so we don't have to redo move on our board)
            self.board.make_move(root.move, root.color)
        else:
            root.value = {}
        if len(root.children) == 0:
            root.value = self.board_points()
            if root.move is not None:
                self.board.undo()
            return root.value
        else:
            if color == self.color:  #MaximizingPlayer
                #val = -999
                for child in root.children:
                    '''
                    val = max(val, rec_abp_v2(child, alpha, beta))
                    alpha = max(alpha, val)
                    '''
                    val = self.rec_abp_v2(child, alpha, beta)
                    if alpha > val:  #Alpha > Val
                        root.alpha = alpha
                    else:  #Val > Alpha
                        alpha = val
                        if root.move is None:  #Root node, ie save the move to get here
                            root.value.setdefault(alpha, []).append(child.move)
                        root.alpha = alpha
                    if alpha >= beta:
                        break
                if root.move is not None:
                    self.board.undo()
                return alpha
            else:  #Minimizing Player
                #val = 999
                for child in root.children:
                    '''
                    val = min(val, alphabeta(child, alpha, beta))
                    beta = min(val, beta)
                    '''
                    val = self.rec_abp_v2(child, alpha, beta)
                    if beta < val:  #Beta < Val
                        root.beta = beta
                    else:
                        beta = val
                        if root.move is None:
                            root.value.setdefault(beta, []).append(child.move)
                        root.beta = beta
                    if alpha >= beta:
                        break
                if root.move is not None:
                    self.board.undo()
                return beta
Esempio n. 16
0
class StudentAI():
    def __init__(self, col, row, p):
        self.col = col
        self.row = row
        self.p = p
        self.board = Board(col, row, p)
        self.board.initialize_game()
        self.color = ''
        self.opponent = {1: 2, 2: 1}
        self.color = 2
        self.depth = 4
        self.a = -math.inf
        self.b = math.inf

    def get_move(self, move):
        if len(move) != 0:
            self.board.make_move(move, self.opponent[self.color])
        else:
            self.color = 1
        moves = self.board.get_all_possible_moves(self.color)

        # index = randint(0, len(moves) - 1)
        # inner_index = randint(0, len(moves[index]) - 1)
        # move = moves[index][inner_index]
        move = self.select_move(moves)
        # print(move)
        self.board.make_move(move, self.color)
        return move

    def select_move(self, moves):
        """take a list of moves and select the best one,
        return move"""
        copySelf = copy.deepcopy(self)

        return abPruning(copySelf, 0, self.a, self.b, self.color)
        # return simple_search(copySelf)

    def heuristic_func(self, move, color):
        """take a move, evaluate it's heuristic value
        return the value"""
        result = 0
        colorDict = {"B": 1, "W": 2, ".": 0}
        self.board.make_move(move, color)
        # print (self.board.board)
        black = []
        white = []
        for row in self.board.board:
            for checker in row:
                if colorDict[checker.get_color()] == 1:
                    black.append(checker.get_location())
                else:
                    white.append(checker.get_location())
        # for row in self.board.board:
        #     for checker in row:
        #         add = 1 if colorDict[checker.get_color()] == self.color else -1
        #         y = checker.get_location()[1]
        #         if self.color == 1:
        #             # if getProtected(checker.get_location(), black) and add == 1:
        #             #     result += 5
        #             if checker.is_king:
        #                 result += (self.board.row + 1 + y) * add
        #             else:
        #                 result += (y + 5) * add
        #         else:
        #             # if getProtected(checker.get_location(), white) and add == 1:
        #             #     result += 5
        #             if checker.is_king:
        #                 result += (self.board.row + 1) * add
        #             else:
        #                 result += (4 + self.board.row - y) * add
        for row in self.board.board:
            for checker in row:
                y = checker.get_location()[1]
                x = checker.get_location()[0]
                if colorDict[checker.get_color()] == self.color:
                    if self.color == 2 and y == self.board.row - 1:
                        result += 0.5
                    elif self.color == 1 and y == 0:
                        result += 0.5
                    if x == 0 or x == self.board.col - 1:
                        result += 0.25
                    if checker.is_king:
                        result += 3.0 + 0.5 * abs(y - (self.board.row / 2.0))
                    else:
                        result += 1.5
                else:
                    if checker.is_king:
                        result -= 3.0
                    else:
                        result -= 1.5

        self.board.undo()
        return result
Esempio n. 17
0
class StudentAI():
    def __init__(self, col, row, p):
        self.col = col
        self.row = row
        self.p = p
        self.board = Board(col, row, p)
        self.board.initialize_game()
        self.color = ''
        self.opponent = {1: 2, 2: 1}
        self.color = 2

    def get_move(self, move):
        if len(move) != 0:
            self.board.make_move(move, self.opponent[
                self.color])  # Run opponent's move for self.board
        else:
            self.color = 1

        root = Tree(self.opponent[self.color])  #Tree root
        self.rec_tree(root, search_depth)
        self.rec_heuristic(root)

        avail_moves = root.value[list(root.value)[0]]
        cur_move = avail_moves[0]
        #print(avail_moves)

        self.board.make_move(cur_move, self.color)  # Make the optimal move
        move = cur_move
        return move

    def ftu(self, color):  #Function to use (min vs max by color)
        if color == self.color:  # Calculate Min
            return max
        else:  # Calculate Max
            return min

    def min_max(self, children,
                color):  # Returns dict -> {Max/min value: Moves to get here}
        ftu = self.ftu(color)  #Use corresponding min or max depending on color
        value_map = {}
        for child in children:
            for v in child.value.keys():
                value_map.setdefault(v, []).append(
                    child.move
                )  # D: {heuristic value: Move to make to get here}
        # print(value_map)
        return {ftu(value_map): value_map[ftu(value_map)]}

    def board_points(
            self):  # 5 + row number for pawns, 5 + row number + 2 for kings
        pts = 0
        for i in range(self.row):
            for j in range(self.col):
                checker = self.board.board[i][j]
                if checker.color == 'B':  # For black side pieces
                    pts += 5 + checker.row
                    if checker.is_king:  # 2 additional pts for king
                        pts += 2
                elif checker.color == 'W':  # FOr white side pieces
                    pts -= 11 - checker.row  # 5 + (6 - Row)
                    if checker.is_king:  # 2 additional pts for king
                        pts -= 2
        return pts if self.color == "B" else -pts

    def print_tree(self, root, level=0):
        # print("PRINTING TREE")

        print("\t" * level, root.value, "->", root.move)
        if len(root.children) != 0:  # Not Leaf node
            for child in root.children:
                self.print_tree(child, level + 1)

    def rec_tree(self, root: Tree, level=1):
        if level == 0:
            pass
        else:
            if root.move is not None:  # Not root of tree
                self.board.make_move(root.move, root.color)
            #Check if win here maybe?
            avail_moves = self.board.get_all_possible_moves(
                self.opponent[root.color])
            for i in range(len(avail_moves)):
                for j in range(len(avail_moves[i])):
                    #print(root)
                    root.children.append(
                        Tree(self.opponent[root.color], avail_moves[i][j]))
            for child in root.children:
                self.rec_tree(child, level - 1)

            if root.move is not None:
                self.board.undo()

    def rec_heuristic(self, root: Tree):
        if root.move is not None:
            self.board.make_move(root.move, root.color)
        if len(root.children) == 0:  #Passed node has no children
            pass  #Evaluate heuristic for board(and return?)
            root.value = {self.board_points(): []}
        else:  #Evaluate rec_heuristic for children, then retrieve values and apply min/max as appropriate
            for child in root.children:
                self.rec_heuristic(child)
            root.value = self.min_max(root.children, root.color)

        if root.move is not None:
            self.board.undo()
Esempio n. 18
0
class StudentAI():
    def __init__(self, col, row, p):
        self.col = col
        self.row = row
        self.p = p
        self.board = Board(col, row, p)
        self.board.initialize_game()
        self.color = ''
        self.opponent = {1: 2, 2: 1}
        self.color = 2

        self.search_depth = 5
        self.debug = True
        self.time_used = 0

        self.transposition_table = dict()

    def get_move(self, move):
        if self.debug:
            current_move_elapsed = time.time()

        if len(move) != 0:
            self.board.make_move(move, self.opponent[self.color])
        else:
            self.color = 1
        moves = self.board.get_all_possible_moves(self.color)

        # index = randint(0,len(moves)-1)
        # inner_index =  randint(0,len(moves[index])-1)
        # move = moves[index][inner_index]

        how_many_moves = 0
        for outer_index in range(len(moves)):
            how_many_moves += len(moves[outer_index])

        if how_many_moves == 1:
            if self.debug:
                self.time_used += (time.time() - current_move_elapsed)
                print("Total elapsed time (in seconds):", self.time_used)
            self.board.make_move(moves[0][0], self.color)
            return moves[0][0]

        depth = round((4 / how_many_moves) + self.search_depth)

        if self.debug:
            print(how_many_moves, "possible moves")
            print("Depth:", depth)

        best_move = None
        best_move_score = -math.inf
        for outer_index in range(len(moves)):
            for inner_index in range(len(moves[outer_index])):
                self.board.make_move(moves[outer_index][inner_index],
                                     self.color)
                move_score = self.search(depth,
                                         moves[outer_index][inner_index],
                                         self.color, -math.inf, math.inf)
                self.board.undo()
                if move_score > best_move_score:
                    best_move_score = move_score
                    best_move = moves[outer_index][inner_index]

        self.board.make_move(best_move, self.color)

        if self.debug:
            self.time_used += (time.time() - current_move_elapsed)
            print("Total elapsed time (in seconds):", self.time_used)

        return best_move

    def search(self, depth, move, turn, alpha, beta):
        current_key = self.get_key()
        if current_key in self.transposition_table.keys(
        ) and depth == self.transposition_table[current_key].depth:
            return self.transposition_table[current_key].value

        winner = self.board.is_win(turn)
        win_return = 1000 + depth
        if winner != 0:
            if winner == -1:
                self.transposition_table[current_key] = TTEntry(0, depth)
                return 0
            if self.color == winner:
                self.transposition_table[current_key] = TTEntry(
                    win_return, depth)
                return win_return
            self.transposition_table[current_key] = TTEntry(-win_return, depth)
            return -win_return
        if depth == 0:
            black = 0
            white = 0
            for x in range(self.board.row):
                for y in range(self.board.col):
                    if self.board.board[x][y].color == "W":
                        white += 5
                        if self.board.board[x][y].is_king:
                            white += self.row + 2
                        else:
                            white += x

                    if self.board.board[x][y].color == "B":
                        black += 5
                        if self.board.board[x][y].is_king:
                            black += self.row + 2
                        else:
                            black += (self.row - x - 1)

            score = black - white
            if self.color == 1:  # 1 = black
                self.transposition_table[current_key] = TTEntry(score, depth)
                return score
            self.transposition_table[current_key] = TTEntry(-score, depth)
            return -score  # 2 = white

        if turn == self.color:  # min
            worst = math.inf
            possible_moves = self.board.get_all_possible_moves(
                self.opponent[self.color])
            for x in range(len(possible_moves)):
                for y in range(len(possible_moves[x])):
                    self.board.make_move(possible_moves[x][y],
                                         self.opponent[self.color])
                    current = self.search(depth - 1, possible_moves[x][y],
                                          self.opponent[self.color], alpha,
                                          beta)
                    self.board.undo()
                    if current < worst:
                        worst = current
                    if current < beta:
                        beta = current
                    if beta <= alpha:
                        break
                else:
                    continue
                break
            self.transposition_table[current_key] = TTEntry(worst, depth)
            return worst

        else:  # max
            best = -math.inf
            possible_moves = self.board.get_all_possible_moves(self.color)
            for x in range(len(possible_moves)):
                for y in range(len(possible_moves[x])):
                    self.board.make_move(possible_moves[x][y], self.color)
                    current = self.search(depth - 1, possible_moves[x][y],
                                          self.color, alpha, beta)
                    self.board.undo()
                    if current > best:
                        best = current
                    if current > alpha:
                        alpha = current
                    if beta <= alpha:
                        break
                else:
                    continue
                break
            self.transposition_table[current_key] = TTEntry(best, depth)
            return best

    def get_key(self):
        key = ""
        for x in range(self.board.row):
            for y in range(self.board.col):
                if self.board.board[x][y].color == "B":
                    key += "1"
                elif self.board.board[x][y].color == "W":
                    key += "2"
                else:
                    key += "0"
        return key
Esempio n. 19
0
class StudentAI():
    def __init__(self, col, row, p):
        self.col = col
        self.row = row
        self.p = p
        self.board = Board(col, row, p)
        self.board.initialize_game()
        self.color = ''
        self.opponent = {1: 2, 2: 1}
        self.color = 2

        self.root = Node(self.color, -1)
        self.start = None

    def flatten(self, ini_list) -> list:
        return sum(ini_list, [])

    def isTimeLeft(self):
        time = datetime.datetime.now()
        if (time - self.start).seconds < turnTimer:
            return True
        return False

    def select(
        self
    ) -> Node:  #REMINDER: moves is the flattened list of all available moves
        maxNode = self.root
        maxUct = -1
        ptr = self.root
        uct = None
        found = False

        while len(ptr.children) != 0:  #Node is not a leaf node
            moves = self.flatten(self.board.get_all_possible_moves(ptr.color))
            for m in moves:
                found = False
                for c in ptr.children:
                    if not found and m == c.move:
                        uct = c.UCT()
                        if uct > maxUct:
                            maxUct = uct
                            maxNode = c
                        found = True
                if not found:
                    return ptr  #Node is a leaf node, return parent to expand later

            if maxNode.move != -1:
                self.board.make_move(maxNode.move, ptr.color)
            ptr = maxNode

        # Node is leaf node
        return ptr  #Same thing as line 135

    def expand(self, node) -> Node:
        moves = self.flatten(self.board.get_all_possible_moves(node.color))
        toMove = moves[0]

        childrenMoves = []
        for c in node.children:
            childrenMoves.append(c.move.seq)
        for m in moves:
            if childrenMoves.count(
                    m.seq
            ) == 0:  #Get all available moves for node, then find the leaf node to expand
                toMove = m
                break

        child = Node(self.opponent[node.color], toMove, node)
        node.children.append(child)
        return child

    def simulate(self, child):
        players = {1: "B", 2: "W"}
        winner = None
        counter = 0
        color = child.color

        while self.board.is_win(players[color]) == 0:
            moves = self.flatten(self.board.get_all_possible_moves(color))
            if len(moves) != 0:  #player has moves
                i = randint(0, len(moves) - 1)
                self.board.make_move(moves[i], color)
                color = self.opponent[color]
                counter += 1
            else:  #player doesnt have moves, but game hasn't ended yet
                color = self.opponent[color]

        winner = self.board.is_win(players[color])
        while counter != 0:
            self.board.undo()
            counter -= 1
        return winner

    def backProp(self, result, child):
        while child is not None:
            child.upSims()
            if result != child.color:
                child.upWins()
            child = child.parent

    def MCTS(self, moves) -> Move:
        while (self.isTimeLeft()):
            parent = self.select()
            expand = self.expand(parent)  #TODO check if expand() returns None
            result = self.simulate(expand)
            self.backProp(result, expand)

        bestMove = None  # self.root.children[i].move
        if len(self.root.children) == 0:
            index = randint(0, len(moves) - 1)
            bestMove = moves[index]
        else:
            bestWR = -1
            i = 0
            while i != len(self.root.children):
                if self.root.children[i].getWinRate() > bestWR:
                    bestWR = self.root.children[i].getWinRate()
                    bestMove = self.root.children[i].move
                i += 1

        return bestMove

    def get_move(self, move):
        if len(move) != 0:
            self.board.make_move(move, self.opponent[self.color])

            if self.root.parent is None:  # len(self.root.children) == 0:
                #what if the root.children doesnt contain the one move we wanted?
                # FIX: checking len of self.root.children to moves of self.root
                self.root.move = move
            else:
                i = 0
                while i != len(self.root.children):
                    if self.root.children[i].move == move:
                        break
                    i += 1
                if i != len(self.root.children):
                    self.root = self.root.children[i]
                else:  #no child node: add it
                    new_root = Node(self.color, move, self.root)
                    self.root.children.append(new_root)
                    self.root = new_root

        else:
            self.color = 1
            self.root.color = 1

        self.start = datetime.datetime.now()
        moves = self.flatten(self.board.get_all_possible_moves(
            self.root.color))
        move = self.MCTS(moves)

        self.board.make_move(move,
                             self.root.color)  # PROBLEM LINE: color mismatch
        # update root to move just picked from MCTS
        i = 0
        while i != len(self.root.children):
            if self.root.children[i].move == move:
                break
            i += 1
        self.root = self.root.children[i]
        return move
Esempio n. 20
0
class StudentAI():
    def __init__(self, col, row, p):
        self.col = col
        self.row = row
        self.p = p
        self.board = Board(col, row, p)
        self.board.initialize_game()
        self.number_of_move = 0
        self.EARLY_GAME = 10
        self.MID_GAME = 20
        self.END_GAME = 30
        self.run_time_depth = 5
        self.opponent = {1: 2, 2: 1}
        self.color = 2

    def get_move(self, move):
        if len(move) != 0:
            self.board.make_move(move, self.opponent[self.color])
        else:
            self.color = 1

        move = self.best_move()

        if self.board.row == self.board.col:
            self.run_time_depth = self.get_depth(True)
        if self.board.row != self.board.col:
            self.run_time_depth = self.get_depth(False)

        self.board.make_move(move, self.color)
        self.number_of_move += 1
        return move

    # ALPHA BETA PRUNE STARTS HERE
    def best_move(self) -> Move:
        moves = self.board.get_all_possible_moves(self.color)
        smart_move = Move([])
        alpha = -math.inf
        beta = math.inf
        for checkers in moves:
            for move in checkers:
                self.board.make_move(move, self.color)
                heuristic = self.alpha_beta_prune(1, self.opponent[self.color],
                                                  alpha, beta)
                self.board.undo()
                if heuristic > alpha:
                    alpha = heuristic
                    smart_move = Move(move)

        return smart_move

    def alpha_beta_prune(self, depth, player, alpha, beta) -> float:
        all_moves = self.board.get_all_possible_moves(player)

        if depth is self.run_time_depth:
            return self.evaluate()

        if not all_moves:
            winplayer = self.board.is_win(self.opponent[player])
            if winplayer == self.color:
                return 100000
            elif winplayer == self.opponent[self.color]:
                return -100000
            else:
                return 0

        if player is self.color:
            current_score = -math.inf
            for checker in all_moves:
                for move in checker:
                    self.board.make_move(move, player)
                    heuristic = self.alpha_beta_prune(depth + 1,
                                                      self.opponent[player],
                                                      alpha, beta)
                    self.board.undo()
                    current_score = max(heuristic, current_score)
                    alpha = max(current_score, alpha)
                    if alpha >= beta:
                        break
            return current_score
        else:
            current_score = math.inf
            for checker in all_moves:
                for move in checker:
                    self.board.make_move(move, player)
                    heuristic = self.alpha_beta_prune(depth + 1,
                                                      self.opponent[player],
                                                      alpha, beta)
                    self.board.undo()
                    current_score = min(heuristic, current_score)
                    beta = min(current_score, beta)
                    if alpha >= beta:
                        break
            return current_score

    # make new function to evaluate king
    def evaluate(self) -> float:
        white_king = 0
        white_chess = 0
        black_king = 0
        black_chess = 0

        white_king_list = list()
        black_king_list = list()

        white_chess_list = list()
        black_chess_list = list()

        for all_checkers in self.board.board:
            for checker in all_checkers:
                if checker.is_king:
                    if checker.color == "W":
                        white_king += 1
                        white_king_list.append(checker)
                    if checker.color == "B":
                        black_king += 1
                        black_king_list.append(checker)
                else:
                    if checker.color == "W":
                        white_chess += 2
                        white_chess_list.append(checker)
                    if checker.color == "B":
                        black_chess += 2
                        black_chess_list.append(checker)

                white_chess, black_chess = self.get_score(
                    checker, white_chess, black_chess)

        king_dis = 1
        if self.color == 1:
            score = self.board.black_count - self.board.white_count + (
                black_king * 8 + black_chess) - (white_chess + white_king * 5)
            for checker in black_king_list:
                for opponent in white_chess_list:
                    king_dis += self.calculate_distance(
                        checker.row, checker.col, opponent.row, opponent.col)
        else:
            score = 1 + self.board.white_count - self.board.black_count + (
                white_king * 8 + white_chess) - (black_chess + black_king * 5)
            for checker in white_king_list:
                for opponent in black_chess_list:
                    king_dis += self.calculate_distance(
                        checker.row, checker.col, opponent.row, opponent.col)

        return score / king_dis

    def calculate_distance(self, first_row, first_col, second_row,
                           second_col) -> float:
        a = abs(first_row - second_row)
        b = abs(first_col - second_col)
        return max(a, b)

    def get_depth(self, is_equal):
        if self.number_of_move != 0:
            if is_equal:
                if self.number_of_move <= 5:
                    return 3
                if self.number_of_move <= self.EARLY_GAME:
                    return 5
                if self.number_of_move <= self.END_GAME:
                    return 7
                return 3
            else:
                if 1 <= self.number_of_move <= 5:
                    return 3
                if 6 <= self.number_of_move <= 10:
                    return 5
                if self.number_of_move % 2 == 0:
                    return 5
                return 3
        return 3

    def get_score(self, checker, white_chess, black_chess):
        if checker.col == 0 or checker.col == self.col - 1:
            if checker.color == "W":
                white_chess += 1
            if checker.color == "B":
                black_chess += 1
        if checker.col - 1 > 0 and checker.row - 1 > 0:
            if self.board.board[checker.row - 1][checker.col - 1].color == "W":
                white_chess += 0.5
            if self.board.board[checker.row - 1][checker.col - 1].color == "B":
                black_chess += 0.5
        if checker.col - 1 > 0 and checker.row + 1 <= self.row - 1:
            if self.board.board[checker.row + 1][checker.col - 1].color == "W":
                white_chess += 0.5
            if self.board.board[checker.row + 1][checker.col - 1].color == "B":
                black_chess += 0.5
        if checker.col + 1 <= self.col - 1 and checker.row - 1 > 0:
            if self.board.is_in_board(checker.row - 1, checker.col + 1):
                if self.board.board[checker.row - 1][checker.col +
                                                     1].color == "W":
                    white_chess += 0.5
                if self.board.board[checker.row - 1][checker.col +
                                                     1].color == "B":
                    black_chess += 0.5
        if checker.col + 1 < self.col - 1 and checker.row + 1 <= self.row - 1:
            if self.board.is_in_board(checker.row + 1, checker.col + 1):
                if self.board.board[checker.row + 1][checker.col + 1] == "W":
                    white_chess += 0.5
                if self.board.board[checker.row + 1][checker.col + 1] == "B":
                    black_chess += 0.5

        return white_chess, black_chess
Esempio n. 21
0
class StudentAI():
    def __init__(self, col, row, p):
        self.col = col
        self.row = row
        self.p = p
        self.board = Board(col, row, p)
        self.board.initialize_game()
        self.color = ''
        self.opponent = {1: 2, 2: 1}
        self.color = 2

    def get_move(self, move):
        if len(move) != 0:
            self.board.make_move(move, self.opponent[self.color])
        else:
            self.color = 1

        moves = self.board.get_all_possible_moves(self.color)
        moveValues = []

        for i in range(len(moves)):
            for j in range(len(moves[i])):
                self.board.make_move(moves[i][j], self.color)
                moveValues.append((moves[i][j],
                                   self.minimax(self.board, 3,
                                                self.opponent[self.color],
                                                float('-inf'), float('inf'))))
                self.board.undo()

        move = max(moveValues, key=lambda x: x[1])[0]
        self.board.make_move(move, self.color)
        return move

    def static_eval(self, boardState):
        blackValue = 0
        whiteValue = 0

        for i in range(boardState.row):
            for j in range(boardState.col):
                checker = boardState.board[i][j]
                if checker.color == '.':
                    continue
                elif checker.color == 'B':
                    if checker.is_king:
                        blackValue += 7 + boardState.row
                    else:
                        blackValue += 5 + checker.row
                else:
                    if checker.is_king:
                        whiteValue += 7 + boardState.row
                    else:
                        whiteValue += 5 + (boardState.row - checker.row)

        if self.color == 1:
            return blackValue - whiteValue
        else:
            return whiteValue - blackValue

    def generate_children(self, player) -> [Board]:
        children = []
        checkers = self.board.get_all_possible_moves(player)

        for moveList in checkers:
            for move in moveList:
                boardCopy = deepcopy(self.board)
                boardCopy.make_move(move, player)
                children.append(boardCopy)
        return children

    def minimax(self, boardState, depth, max_player, alpha, beta):
        if depth == 0 or boardState.is_win(max_player):
            return self.static_eval(boardState)

        if max_player:
            best = float('-inf')
            for child in self.generate_children(self.color):
                candidate = self.minimax(child, depth - 1, False, alpha, beta)
                best = max(best, candidate)
                alpha = max(alpha, candidate)
                if alpha >= beta:
                    break
            return best
        else:
            best = float('inf')
            for child in self.generate_children(self.opponent[self.color]):
                candidate = self.minimax(child, depth - 1, True, alpha, beta)
                best = min(best, candidate)
                beta = min(beta, candidate)
                if alpha >= beta:
                    break
            return best
Esempio n. 22
0
class StudentAI():
    def __init__(self, col, row, p):
        self.col = col
        self.row = row
        self.p = p
        self.board = Board(col, row, p)
        self.board.initialize_game()
        self.color = ''
        self.opponent = {1: 2, 2: 1}
        self.color = 2

    def num_pieces(self, color):
        if color == 1:
            openent_pieces = self.board.black_count
        elif color == 2:
            openent_pieces = self.board.white_count
        return openent_pieces

    def get_max(self, depth, iterations):
        opp_color = self.opponent[self.color]
        #best = (0,0)
        if depth == iterations:
            moves = self.board.get_all_possible_moves(self.color)
            opponent_pieces = self.num_pieces(
                opp_color)  #number of opponent pieces before our move
            we_eat = 0
            #selects max taken pieces
            for x, checker in enumerate(moves):
                for v, move in enumerate(moves[x]):
                    self.board.make_move(moves[x][v],
                                         self.color)  # we make second move
                    temp = opponent_pieces - self.num_pieces(opp_color)
                    if temp > we_eat:
                        we_eat = temp
                        #best = (x,v)
                    self.board.undo()
            return (we_eat, 0)
        else:
            moves = self.board.get_all_possible_moves(self.color)
            max = -(self.num_pieces(self.color))
            our_pieces = self.num_pieces(
                self.color)  # our pieces before our move
            for i, checker in enumerate(moves):
                for j, move in enumerate(moves[i]):
                    self.board.make_move(moves[i][j], self.color)  #Make move
                    min = self.get_min(depth + 1, iterations)
                    if min > max:
                        max = min
                        best = (i, j)
                    elif min == max:
                        if (randint(0, 1)):
                            best = (i, j)
                    self.board.undo()
            return (max, best)

    def get_min(self, depth, iterations):
        min_color = self.opponent[self.color]
        min = self.num_pieces(min_color)
        if depth == iterations - 1:
            min_moves = self.board.get_all_possible_moves(min_color)
            maxs_pieces = self.num_pieces(self.color)
            for x, checker in enumerate(min_moves):
                for y, move in enumerate(min_moves[x]):
                    self.board.make_move(min_moves[x][y], min_color)
                    min_eats = maxs_pieces - self.num_pieces(self.color)
                    max_eats, b = self.get_max(depth + 1, iterations)
                    diff = max_eats - min_eats
                    if diff < min:
                        min = diff
                    self.board.undo()
            return min
        else:
            min_moves = self.board.get_all_possible_moves(min_color)
            for x, checker in enumerate(min_moves):
                for y, move in enumerate(min_moves[x]):
                    self.board.make_move(min_moves[x][y], min_color)
                    max_eats, b = self.get_max(depth + 1, iterations)
                    if max_eats < min:
                        min = max_eats
                    self.board.undo()
            return min

    def get_move(self, move):
        opp_color = self.opponent[self.color]
        if len(move) != 0:
            self.board.make_move(move, self.opponent[self.color])
        else:
            self.color = 1
        moves = self.board.get_all_possible_moves(self.color)
        max, best = self.get_max(1, 3)
        '''
        if max == 0 and len(self.board.get_all_possible_moves(opp_color)) < 3:
            ## Idea to improve choice, if max is tied look at the move which will get us closes
            ## closest to opponent piece
            inner = i
            distance = 100
            checker = self.board.get_all_possible_moves(opp_color)
            opp_checker = checker[0][0].seq[0]
            #for i,checker in enumerate(moves1):
            for j,move in enumerate(moves1[0]):
                temp_dist = math.sqrt(sum([(a - b) ** 2 for a, b in zip(move[0], opp_checker)]))
                if temp_dist < distance:
                    best = (0,j)
        '''
        move = moves[best[0]][best[1]]
        self.board.make_move(move, self.color)
        return move