Ejemplo n.º 1
0
def miniMax(side, board, flags, depth=DEPTH, alpha=-INF, beta=INF):
    if depth == 0:
        return evaluate(board)

    if not side:
        bestVal = -INF
        for fro, to in legalMoves(side, board, flags):
            movedata = makeMove(side, board, fro, to, flags)
            nodeVal = miniMax(*movedata, depth - 1, alpha, beta)
            if nodeVal > bestVal:
                bestVal = nodeVal
                if depth == DEPTH:
                    bestMove = (fro, to)
            alpha = max(alpha, bestVal)
            if alpha >= beta:
                break

    else:
        bestVal = INF
        for fro, to in legalMoves(side, board, flags):
            movedata = makeMove(side, board, fro, to, flags)
            nodeVal = miniMax(*movedata, depth - 1, alpha, beta)
            if nodeVal < bestVal:
                bestVal = nodeVal
                if depth == DEPTH:
                    bestMove = (fro, to)
            beta = min(beta, bestVal)
            if alpha >= beta:
                break

    if depth == DEPTH:
        return bestMove
    else:
        return bestVal
Ejemplo n.º 2
0
def alphabeta(side, board, flags, depth, alpha=-math.inf, beta=math.inf):
    '''
    Return minimax-optimal move sequence, and a tree that exhibits alphabeta pruning.
    Return: (value, moveList, moveTree)
      value (float): value of the final board in the minimax-optimal move sequence
      moveList (list): the minimax-optimal move sequence, as a list of moves
      moveTree (dict: encode(*move)->dict): a tree of moves that were evaluated in the search process
    Input:
      side (boolean): True if player1 (Min) plays next, otherwise False
      board (2-tuple of lists): current board layout, used by generateMoves and makeMove
      flags (list of flags): list of flags, used by generateMoves and makeMove
      depth (int >=0): depth of the search (number of moves)
    '''
    moves = [move for move in generateMoves(side, board, flags)]
    if depth == 0 or len(moves) == 0:
        return (evaluate(board), [], {})
    if side == False:
        move_tree = {}
        move_list = []
        target = -math.inf
        for move in moves:
            newside, newboard, newflags = makeMove(side, board, move[0],
                                                   move[1], flags, move[2])
            value, l, t = alphabeta(newside, newboard, newflags, depth - 1,
                                    alpha, beta)
            move_tree[encode(*move)] = t

            if value > target:
                target = value
                alpha = max(target, alpha)
                max_move = move
                move_list = l
                if alpha >= beta:
                    break
        move_list.insert(0, max_move)

        return (target, move_list, move_tree)
    else:
        move_tree = {}
        move_list = []
        target = math.inf
        for move in moves:
            newside, newboard, newflags = makeMove(side, board, move[0],
                                                   move[1], flags, move[2])
            value, l, t = alphabeta(newside, newboard, newflags, depth - 1,
                                    alpha, beta)
            move_tree[encode(*move)] = t

            if value < target:
                target = value
                beta = min(target, beta)
                min_move = move
                move_list = l
                if alpha >= beta:
                    break
        move_list.insert(0, min_move)

        return (target, move_list, move_tree)
Ejemplo n.º 3
0
def alphabeta(side, board, flags, depth, alpha=-math.inf, beta=math.inf):
    '''
    Return minimax-optimal move sequence, and a tree that exhibits alphabeta pruning.
    Return: (value, moveList, moveTree)
      value (float): value of the final board in the minimax-optimal move sequence
      moveList (list): the minimax-optimal move sequence, as a list of moves
      moveTree (dict: encode(*move)->dict): a tree of moves that were evaluated in the search process
    Input:
      side (boolean): True if player1 (Min) plays next, otherwise False
      board (2-tuple of lists): current board layout, used by generateMoves and makeMove
      flags (list of flags): list of flags, used by generateMoves and makeMove
      depth (int >=0): depth of the search (number of moves)
    '''

    moves = [move for move in generateMoves(side, board, flags)]
    moveTree = {}
    if len(moves) == 0 or depth == 0:
        return evaluate(board), [], moveTree

    best_val = None
    best_movelist = []
    if side:  # Min
        best_val = math.inf
        for move in moves:
            newside, newboard, newflags = makeMove(side, board, move[0],
                                                   move[1], flags, move[2])
            poss_val, poss_movelist, poss_movetree = alphabeta(
                newside, newboard, newflags, depth - 1, alpha, beta)

            moveTree[encode(*move)] = poss_movetree

            if poss_val < best_val:
                best_val = poss_val
                best_movelist = [move] + poss_movelist

            beta = min(beta, best_val)
            if beta <= alpha:
                return best_val, best_movelist, moveTree

    else:  # Max
        best_val = -math.inf
        for move in moves:
            newside, newboard, newflags = makeMove(side, board, move[0],
                                                   move[1], flags, move[2])
            poss_val, poss_movelist, poss_movetree = alphabeta(
                newside, newboard, newflags, depth - 1, alpha, beta)

            moveTree[encode(*move)] = poss_movetree

            if poss_val > best_val:
                best_val = poss_val
                best_movelist = [move] + poss_movelist

            alpha = max(alpha, best_val)
            if alpha >= beta:
                return best_val, best_movelist, moveTree

    return best_val, best_movelist, moveTree
Ejemplo n.º 4
0
def minimax(side, board, flags, depth):
    '''
    Return a minimax-optimal move sequence, tree of all boards evaluated, and value of best path.
    Return: (value, moveList, moveTree)
      value (float): value of the final board in the minimax-optimal move sequence
      moveList (list): the minimax-optimal move sequence, as a list of moves
      moveTree (dict: encode(*move)->dict): a tree of moves that were evaluated in the search process
    Input:
      side (boolean): True if player1 (Min) plays next, otherwise False
      board (2-tuple of lists): current board layout, used by generateMoves and makeMove
      flags (list of flags): list of flags, used by generateMoves and makeMove
      depth (int >=0): depth of the search (number of moves)
    '''
    value = math.inf if side == True else -math.inf
    moveList = []
    moveTree = {}
    bestMove = []
    # create minimax tree move:(next move, heuristic)
    # bubble up from leaves
    # base case
    if depth == 1:
        # find all possible moves at board state
        for move in generateMoves(side, board, flags):
            moveTree[encode(move[0], move[1], move[2])] = {}
            newside, newboard, newflags = makeMove(side, board, move[0],
                                                   move[1], flags, move[2])
            # check if move is optimal
            if side == True and evaluate(
                    newboard
            ) < value:  # min player which means wants lowest value
                value = evaluate(newboard)
                bestMove = move
            elif side == False and evaluate(newboard) > value:
                value = evaluate(newboard)
                bestMove = move
        moveList.append(bestMove)
        return value, moveList, moveTree

    for move in generateMoves(side, board, flags):
        moveTree[encode(move[0], move[1], move[2])] = {}
        newside, newboard, newflags = makeMove(side, board, move[0], move[1],
                                               flags, move[2])
        minmax = minimax(newside, newboard, newflags, depth - 1)
        moveTree[encode(move[0], move[1], move[2])] = minmax[2]
        moves = minmax[2]
        if side == True and minmax[
                0] < value:  # min player which means wants lowest value
            value = minmax[0]
            bestMove = move
            moveList = minmax[1]
        elif side == False and minmax[0] > value:
            value = minmax[0]
            bestMove = move
            moveList = minmax[1]
    moveList.insert(0, bestMove)
    return value, moveList, moveTree
Ejemplo n.º 5
0
def stochastic(side, board, flags, depth, breadth, chooser):
    '''
    Choose the best move based on breadth randomly chosen paths per move, of length depth-1.
    Return: (value, moveList, moveTree)
      value (float): average board value of the paths for the best-scoring move
      moveLists (list): any sequence of moves, of length depth, starting with the best move
      moveTree (dict: encode(*move)->dict): a tree of moves that were evaluated in the search process
    Input:
      side (boolean): True if player1 (Min) plays next, otherwise False
      board (2-tuple of lists): current board layout, used by generateMoves and makeMove
      flags (list of flags): list of flags, used by generateMoves and makeMove
      depth (int >=0): depth of the search (number of moves)
      breadth: number of different paths
      chooser: a function similar to random.choice, but during autograding, might not be random.
    '''
    value = 0
    moveList = []
    moveTree = {}
    initialMoves = []
    if side == True:  # black moves
        for move in generateMoves(side, board, flags):
            newside, newboard, newflags = makeMove(side, board, move[0],
                                                   move[1], flags, move[2])
            moveTree[encode(move[0], move[1], move[2])] = {}
            # print("\nmove: ",move,"\nvalue: ",evaluate(newboard))
            # find all possible next moves
            random = stochastic_helper(newside, newboard, newflags, depth - 1,
                                       breadth, chooser, depth)
            # print("next value: ",random[0])
            moveTree[encode(move[0], move[1], move[2])] = random[2]
            random[1].insert(0, move)
            initialMoves.append((random[0], random[1]))
        value, moveList = min(initialMoves)
    else:  # white moves
        for move in generateMoves(side, board, flags):
            newside, newboard, newflags = makeMove(side, board, move[0],
                                                   move[1], flags, move[2])
            moveTree[encode(move[0], move[1], move[2])] = {}
            # find all possible next moves
            random = stochastic_helper(newside, newboard, newflags, depth - 1,
                                       breadth, chooser, depth)
            moveTree[encode(move[0], move[1], move[2])] = random[2]
            random[1].insert(0, move)
            initialMoves.append((random[0], random[1]))
        value, moveList = max(initialMoves)
    # print(initialMoves)
    # print(min(initialMoves))
    # print(value)
    # print(moveList)
    # print(moveTree)
    return value, moveList, moveTree
Ejemplo n.º 6
0
def minimax(side, board, flags, depth):
    '''
    Return a minimax-optimal move sequence, tree of all boards evaluated, and value of best path.
    Return: (value, moveList, moveTree)
      value (float): value of the final board in the minimax-optimal move sequence
      moveList (list): the minimax-optimal move sequence, as a list of moves
      moveTree (dict: encode(*move)->dict): a tree of moves that were evaluated in the search process
    Input:
      side (boolean): True if player1 (Min) plays next, otherwise False
      board (2-tuple of lists): current board layout, used by generateMoves and makeMove
      flags (list of flags): list of flags, used by generateMoves and makeMove
      depth (int >=0): depth of the search (number of moves)
    '''
    moves = [move for move in generateMoves(side, board, flags)]
    if depth == 0 or len(moves) == 0:
        return (evaluate(board), [], {})
    if not side:
        max_value = -math.inf
        move_tree = {}
        move_list = []
        for move in moves:
            newside, newboard, newflags = makeMove(side, board, move[0],
                                                   move[1], flags, move[2])
            value, l, t = minimax(newside, newboard, newflags, depth - 1)
            move_tree[encode(*move)] = t
            if value > max_value:
                max_value = value
                max_move = move
                move_list = l
        move_list.insert(0, max_move)

        return (max_value, move_list, move_tree)
    else:
        min_value = math.inf
        move_tree = {}
        move_list = []
        for move in moves:
            newside, newboard, newflags = makeMove(side, board, move[0],
                                                   move[1], flags, move[2])
            value, l, t = minimax(newside, newboard, newflags, depth - 1)
            move_tree[encode(*move)] = t
            if value < min_value:
                min_value = value
                min_move = move
                move_list = l
        move_list.insert(0, min_move)

        return (min_value, move_list, move_tree)
Ejemplo n.º 7
0
def convertMoves(moves):
    side, board, flags = initBoardVars()

    for fro, to, promote in map(decode, moves):
        side, board, flags = makeMove(side, board, fro, to, flags, promote)

    return side, board, flags
Ejemplo n.º 8
0
def stochastic(side, board, flags, depth, breadth, chooser):
    '''
    Choose the best move based on breadth randomly chosen paths per move, of length depth-1.
    Return: (value, moveList, moveTree)
      value (float): average board value of the paths for the best-scoring move
      moveLists (list): any sequence of moves, of length depth, starting with the best move
      moveTree (dict: encode(*move)->dict): a tree of moves that were evaluated in the search process
    Input:
      side (boolean): True if player1 (Min) plays next, otherwise False
      board (2-tuple of lists): current board layout, used by generateMoves and makeMove
      flags (list of flags): list of flags, used by generateMoves and makeMove
      depth (int >=0): depth of the search (number of moves)
      breadth: number of different paths 
      chooser: a function similar to random.choice, but during autograding, might not be random.
    '''

    moves = [move for move in generateMoves(side, board, flags)]
    moveList = []
    moveTree = {}
    if len(moves) == 0 or depth == 0 or breadth == 0:
        return evaluate(board), moveList, moveTree

    init_values = []
    init_move_lists = []
    for move in moves:
        init_side, init_board, init_flags = makeMove(side, board, move[0],
                                                     move[1], flags, move[2])
        init_movetree = {}

        value_sum = 0
        rand_movelist = None
        for i in range(breadth):
            val, rand_movelist, rand_movetree = stoch_path(
                init_side, init_board, init_flags, depth - 1, chooser)
            value_sum += val
            init_movetree.update(rand_movetree)

        init_values.append(value_sum / breadth)
        init_move_lists.append([move])
        moveTree[encode(*move)] = init_movetree

    if side:  # Min
        min_val = math.inf
        min_path = None
        for i in range(len(init_values)):
            if init_values[i] < min_val:
                min_val = init_values[i]
                min_path = init_move_lists[i]

        return min_val, min_path, moveTree

    else:  # Max
        max_val = -math.inf
        max_path = None
        for i in range(len(init_values)):
            if init_values[i] > max_val:
                max_val = init_values[i]
                max_path = init_move_lists[i]

        return max_val, max_path, moveTree
Ejemplo n.º 9
0
def random(side, board, flags, chooser):
    '''
    Return a random move, resulting board, and value of the resulting board.
    Return: (value, moveList, boardList)
      value (int or float): value of the board after making the chosen move
      moveList (list): list with one element, the chosen move
      moveTree (dict: encode(*move)->dict): a tree of moves that were evaluated in the search process
    Input:
      side (boolean): True if player1 (Min) plays next, otherwise False
      board (2-tuple of lists): current board layout, used by generateMoves and makeMove
      flags (list of flags): list of flags, used by generateMoves and makeMove
      chooser: a function similar to random.choice, but during autograding, might not be random.
    '''
    moves = [move for move in generateMoves(side, board, flags)]
    print('moves: ', moves)
    if len(moves) > 0:
        move = chooser(moves)
        newside, newboard, newflags = makeMove(side, board, move[0], move[1],
                                               flags, move[2])
        value = evaluate(newboard)
        print('value: ', value)
        print('[move]: ', [move])
        print('{ encode(*move): {} }: ', {encode(*move): {}})
        return (value, [move], {encode(*move): {}})
    else:
        return (evaluate(board), [], {})
Ejemplo n.º 10
0
def stochastic_helper(side, board, flags, level, breadth, chooser, depth):
    value = 0
    moveList = []
    moveTree = {}
    moves = []
    initialMoves = []
    # base case
    if level == 0:
        return evaluate(board), moveList, moveTree
    # create list of possible moves for chooser
    for move in generateMoves(side, board, flags):
        moves.append(move)
    if level == depth - 1:
        for i in range(breadth):
            move = chooser(moves)
            newside, newboard, newflags = makeMove(side, board, move[0],
                                                   move[1], flags, move[2])
            random = stochastic_helper(newside, newboard, newflags, level - 1,
                                       breadth, chooser, depth)
            moveTree[encode(move[0], move[1], move[2])] = random[2]
            random[1].insert(0, move)
            initialMoves.append((random[0], random[1]))
            # value = random[0] + evaluate(board)
            # moveList = random[1].copy()
            # moveList.insert(0,move)
        # print(initialMoves)
        for next in initialMoves:
            value += next[0]
        value = value / breadth
        if side == True:
            moveList = min(initialMoves)[1]
        else:
            moveList = max(initialMoves)[1]
        # print(initialMoves)
    else:
        move = chooser(moves)
        newside, newboard, newflags = makeMove(side, board, move[0], move[1],
                                               flags, move[2])
        random = stochastic_helper(newside, newboard, newflags, level - 1,
                                   breadth, chooser, depth)
        moveTree[encode(move[0], move[1], move[2])] = random[2]
        value = random[0]
        moveList = random[1].copy()
        moveList.insert(0, move)
    return value, moveList, moveTree
Ejemplo n.º 11
0
def minimax(side, board, flags, depth):
    '''
    Return a minimax-optimal move sequence, tree of all boards evaluated, and value of best path.
    Return: (value, moveList, moveTree)
      value (float): value of the final board in the minimax-optimal move sequence
      moveList (list): the minimax-optimal move sequence, as a list of moves
      moveTree (dict: encode(*move)->dict): a tree of moves that were evaluated in the search process
    Input:
      side (boolean): True if player1 (Min) plays next, otherwise False
      board (2-tuple of lists): current board layout, used by generateMoves and makeMove
      flags (list of flags): list of flags, used by generateMoves and makeMove
      depth (int >=0): depth of the search (number of moves)
    '''

    # Base case
    if depth == 0:
        return evaluate(board), [], {}

    moves = {}
    moveTrees = {}
    moveLists = {}

    # Go through all possible moves
    for move in generateMoves(side, board, flags):

        newside, newboard, newflags = makeMove(side, board, move[0], move[1],
                                               flags, move[2])

        # Fan out next level of tree
        score, moveList, moveTree = minimax(newside, newboard, newflags,
                                            depth - 1)

        moves[encode(*move)] = score
        moveTrees[encode(*move)] = moveTree
        moveLists[encode(*move)] = moveList

    # Choose move based on side optimization
    if len(moves) > 0:
        if side:
            best_move = min(moves, key=moves.get)
        else:
            best_move = max(moves, key=moves.get)
    else:
        return evaluate(board), [], {}

    newMoveList = []
    if moveLists[best_move] != None and len(moveLists[best_move]) >= 0:

        newMoveList = [decode(best_move), *moveLists[best_move]]

    # print(moves)
    # print(moveTrees)
    return moves[best_move], newMoveList, moveTrees
Ejemplo n.º 12
0
def stoch_path(side, board, flags, depth, chooser):
    moves = [move for move in generateMoves(side, board, flags)]
    moveList = []
    moveTree = {}
    if depth == 0 or len(moves) == 0:
        return evaluate(board), moveList, moveTree

    rand_move = chooser(moves)
    newside, newboard, newflags = makeMove(side, board, rand_move[0],
                                           rand_move[1], flags, rand_move[2])
    path_val, path_list, path_tree = stoch_path(newside, newboard, newflags,
                                                depth - 1, chooser)

    moveTree[encode(*rand_move)] = path_tree
    moveList = [rand_move] + path_list

    return path_val, moveList, moveTree
Ejemplo n.º 13
0
def stochasticPath(side, board, flags, depth, breadth, chooser):

    if depth == 0:
        return evaluate(board), [], {}

    movesTree = {}
    # for some reason it needs a list
    possibleMoves = [move for move in generateMoves(side, board, flags)]
    thisMove = chooser(possibleMoves)
    fro, to, promote = thisMove
    newSide, newBoard, newFlag = makeMove(side, board, fro, to, flags, promote)
    newVal, newMoveList, newMoveTree = stochasticPath(newSide, newBoard,
                                                      newFlag, depth - 1,
                                                      breadth, chooser)
    movesTree[encode(*thisMove)] = newMoveTree
    movesList = [thisMove] + newMoveList
    return newVal, movesList, movesTree
Ejemplo n.º 14
0
def convertMoves(moves):
    side = 0
    board = ([
        [1, 7, "p"],
        [2, 7, "p"],
        [3, 7, "p"],
        [4, 7, "p"],
        [5, 7, "p"],
        [6, 7, "p"],
        [7, 7, "p"],
        [8, 7, "p"],
        [1, 8, "r"],
        [2, 8, "n"],
        [3, 8, "b"],
        [4, 8, "q"],
        [5, 8, "k"],
        [6, 8, "b"],
        [7, 8, "n"],
        [8, 8, "r"],
    ], [
        [1, 2, "p"],
        [2, 2, "p"],
        [3, 2, "p"],
        [4, 2, "p"],
        [5, 2, "p"],
        [6, 2, "p"],
        [7, 2, "p"],
        [8, 2, "p"],
        [1, 1, "r"],
        [2, 1, "n"],
        [3, 1, "b"],
        [4, 1, "q"],
        [5, 1, "k"],
        [6, 1, "b"],
        [7, 1, "n"],
        [8, 1, "r"],
    ])
    flags = [[True for _ in range(4)], None]

    movelist = map(decode, filter(lambda x: x != "", moves.strip().split(" ")))

    for fro, to, promote in movelist:
        side, board, flags = makeMove(side, board, fro, to, flags, promote)

    return side, board, flags
Ejemplo n.º 15
0
    def findPath(side, board, flags, depth, chooser):
        # print(depth)
        if depth == 0:
            return evaluate(board), [], {}
        else:
            moves = []
            for move in generateMoves(side, board, flags):
                moves.append(move)

            if len(moves) == 0:
                return evaluate(board), [], {}

            chosen_move = chooser(moves)

            newside, newboard, newflags = makeMove(side, board, chosen_move[0],
                                                   chosen_move[1], flags,
                                                   chosen_move[2])

            score, path, tree = findPath(newside, newboard, newflags,
                                         depth - 1, chooser)
            # print(path, [chosen_move, *path])
            return score, [chosen_move, *path], {encode(*chosen_move): tree}
Ejemplo n.º 16
0
def stochastic(side, board, flags, depth, breadth, chooser):
    '''
    Choose the best move based on breadth randomly chosen paths per move, of length depth-1.
    Return: (value, moveList, moveTree)
      value (float): average board value of the paths for the best-scoring move
      moveLists (list): any sequence of moves, of length depth, starting with the best move
      moveTree (dict: encode(*move)->dict): a tree of moves that were evaluated in the search process
    Input:
      side (boolean): True if player1 (Min) plays next, otherwise False
      board (2-tuple of lists): current board layout, used by generateMoves and makeMove
      flags (list of flags): list of flags, used by generateMoves and makeMove
      depth (int >=0): depth of the search (number of moves)
      breadth: number of different paths 
      chooser: a function similar to random.choice, but during autograding, might not be random.
    '''

    possibleMoves = generateMoves(side, board, flags)
    bestAvgValue = 99999
    movesTree = {}
    for move in possibleMoves:
        totalVal = 0
        fro, to, promote = move
        firstSide, firstBoard, firstFlag = makeMove(side, board, fro, to,
                                                    flags, promote)
        movesTree[(encode(*move))] = {}
        for i in range(breadth):
            curVal, moveList, moveTree = stochasticPath(
                firstSide, firstBoard, firstFlag, depth - 1, breadth, chooser)
            for key in moveTree:
                movesTree[(encode(*move))][key] = moveTree[key]
            totalVal += curVal
        avgVal = float(totalVal) / float(breadth)
        if avgVal < bestAvgValue:
            bestAvgValue = avgVal
            bestMove = [move] + moveList

    return bestAvgValue, bestMove, movesTree
Ejemplo n.º 17
0
def minimax(side, board, flags, depth):
    '''
    Return a minimax-optimal move sequence, tree of all boards evaluated, and value of best path.
    Return: (value, moveList, moveTree)
      value (float): value of the final board in the minimax-optimal move sequence
      moveList (list): the minimax-optimal move sequence, as a list of moves
      moveTree (dict: encode(*move)->dict): a tree of moves that were evaluated in the search process
    Input:
      side (boolean): True if player1 (Min) plays next, otherwise False
      board (2-tuple of lists): current board layout, used by generateMoves and makeMove
      flags (list of flags): list of flags, used by generateMoves and makeMove
      depth (int >=0): depth of the search (number of moves)
    '''

    if depth == 0:
        return evaluate(board), [], {}

    possibleMoves = generateMoves(side, board, flags)

    # maximizing player
    if side == 0:

        maxVal = -9999999999
        movesTree = {}
        moveList = []

        for move in possibleMoves:

            fro, to, promote = move
            newSide, newBoard, newFlag = makeMove(side, board, fro, to, flags,
                                                  promote)
            curVal, curMoveList, curMoveTree = minimax(newSide, newBoard,
                                                       newFlag, depth - 1)
            movesTree[encode(*move)] = curMoveTree
            curMax = maxVal
            maxVal = max(curVal, maxVal)
            if maxVal > curMax:
                movesList = [move] + curMoveList

        return maxVal, movesList, movesTree

    # minimizing player
    else:

        minVal = 9999999999
        movesTree = {}
        moveList = []

        for move in possibleMoves:
            fro, to, promote = move
            newSide, newBoard, newFlag = makeMove(side, board, fro, to, flags,
                                                  promote)
            curVal, curMoveList, curMoveTree = minimax(newSide, newBoard,
                                                       newFlag, depth - 1)
            movesTree[encode(*move)] = curMoveTree
            curMin = minVal
            minVal = min(minVal, curVal)
            if minVal < curMin:
                moveList = [move] + curMoveList

        return minVal, moveList, movesTree
Ejemplo n.º 18
0
def alphabeta(side, board, flags, depth, alpha=-math.inf, beta=math.inf):
    '''
    Return minimax-optimal move sequence, and a tree that exhibits alphabeta pruning.
    Return: (value, moveList, moveTree)
      value (float): value of the final board in the minimax-optimal move sequence
      moveList (list): the minimax-optimal move sequence, as a list of moves
      moveTree (dict: encode(*move)->dict): a tree of moves that were evaluated in the search process
    Input:
      side (boolean): True if player1 (Min) plays next, otherwise False
      board (2-tuple of lists): current board layout, used by generateMoves and makeMove
      flags (list of flags): list of flags, used by generateMoves and makeMove
      depth (int >=0): depth of the search (number of moves)
    '''
    value = 0
    moveList = []
    moveTree = {}
    if depth == 0:
        return evaluate(board), moveList, moveTree
    if side == False:  # max/white
        value = -math.inf
        for move in generateMoves(side, board, flags):
            newside, newboard, newflags = makeMove(side, board, move[0],
                                                   move[1], flags, move[2])
            alphabet = alphabeta(newside, newboard, newflags, depth - 1, alpha,
                                 beta)
            moveTree[encode(move[0], move[1], move[2])] = alphabet[2]
            if alphabet[0] > value:
                value = alphabet[0]
                alphabet[1].insert(0, move)
                moveList = alphabet[1].copy()
            alpha = max(alpha, value)
            if alpha >= beta:  # prune check
                break

        # print("depth: ",depth)
        # print(value)
        # print(moveList)
        # # print(moveTree)
        # print("list len: ",len(moveList))
        return value, moveList, moveTree
    else:  # min/black
        value = math.inf
        for move in generateMoves(side, board, flags):
            newside, newboard, newflags = makeMove(side, board, move[0],
                                                   move[1], flags, move[2])
            alphabet = alphabeta(newside, newboard, newflags, depth - 1, alpha,
                                 beta)
            moveTree[encode(move[0], move[1], move[2])] = alphabet[2]
            if alphabet[0] < value:
                value = alphabet[0]
                alphabet[1].insert(0, move)
                moveList = alphabet[1].copy()
            beta = min(beta, value)
            if beta <= alpha:  # prune check
                break

        # print("depth: ", depth)
        # print(value)
        # print(moveTree)
        # print(moveList)
        return value, moveList, moveTree
Ejemplo n.º 19
0
def stochastic(side, board, flags, depth, breadth, chooser):
    '''
    Choose the best move based on breadth randomly chosen paths per move, of length depth-1.
    Return: (value, moveList, moveTree)
      value (float): average board value of the paths for the best-scoring move
      moveLists (list): any sequence of moves, of length depth, starting with the best move
      moveTree (dict: encode(*move)->dict): a tree of moves that were evaluated in the search process
    Input:
      side (boolean): True if player1 (Min) plays next, otherwise False
      board (2-tuple of lists): current board layout, used by generateMoves and makeMove
      flags (list of flags): list of flags, used by generateMoves and makeMove
      depth (int >=0): depth of the search (number of moves)
      breadth: number of different paths 
      chooser: a function similar to random.choice, but during autograding, might not be random.
    '''

    initial_moves = [move for move in generateMoves(side, board, flags)]
    path_avg_score = []
    moveTree = {}
    moveList = []
    moveList_dict = {}
    for m in range(len(initial_moves)):
        newside, newboard, newflags = makeMove(side, board,
                                               initial_moves[m][0],
                                               initial_moves[m][1], flags,
                                               initial_moves[m][2])
        s = newside
        b = newboard
        f = newflags
        breadth_score = []
        curr_list = []
        curr_list.append(initial_moves[m])
        moveTree[encode(*initial_moves[m])] = {}
        for i in range(breadth):
            newside = s
            newflags = f
            newboard = b
            curr_dict = moveTree[encode(*initial_moves[m])]
            for j in range(depth - 1):
                random_moves = [
                    move for move in generateMoves(newside, newboard, newflags)
                ]
                random_move = chooser(random_moves)
                if i == 0:
                    curr_list.append(random_move)
                curr_dict[encode(*random_move)] = {}
                curr_dict = curr_dict[encode(*random_move)]
                newside, newboard, newflags = makeMove(newside, newboard,
                                                       random_move[0],
                                                       random_move[1],
                                                       newflags,
                                                       random_move[2])
            final_score = evaluate(newboard)
            breadth_score.append(final_score)
        average = sum(breadth_score) / breadth
        path_avg_score.append(average)
        moveList_dict[encode(*initial_moves[m])] = curr_list

    if side:
        best_average = min(path_avg_score)
        best_index = path_avg_score.index(best_average)
    else:
        best_average = max(path_avg_score)
        best_index = path_avg_score.index(best_average)

    moveList = []
    best_move = initial_moves[best_index]
    moveList = moveList_dict[encode(*best_move)]

    return best_average, moveList, moveTree
Ejemplo n.º 20
0
def alphabeta(side, board, flags, depth, alpha=-math.inf, beta=math.inf):
    '''
    Return minimax-optimal move sequence, and a tree that exhibits alphabeta pruning.
    Return: (value, moveList, moveTree)
      value (float): value of the final board in the minimax-optimal move sequence
      moveList (list): the minimax-optimal move sequence, as a list of moves
      moveTree (dict: encode(*move)->dict): a tree of moves that were evaluated in the search process
    Input:
      side (boolean): True if player1 (Min) plays next, otherwise False
      board (2-tuple of lists): current board layout, used by generateMoves and makeMove
      flags (list of flags): list of flags, used by generateMoves and makeMove
      depth (int >=0): depth of the search (number of moves)
    '''
    # Base case
    if depth == 0:
        return evaluate(board), [], {}

    moves = {}
    moveTrees = {}
    moveLists = {}

    if side:
        value = math.inf
        # Go through all possible moves
        for move in generateMoves(side, board, flags):

            newside, newboard, newflags = makeMove(side, board, move[0],
                                                   move[1], flags, move[2])

            # Fan out next level of tree
            score, moveList, moveTree = alphabeta(newside, newboard, newflags,
                                                  depth - 1, alpha, beta)

            moves[encode(*move)] = score
            moveTrees[encode(*move)] = moveTree
            moveLists[encode(*move)] = moveList

            value = value if value < score else score
            beta = beta if beta < value else value

            if (beta <= alpha):
                return score, [], moveTrees

    else:
        value = -math.inf
        # Go through all possible moves
        for move in generateMoves(side, board, flags):

            newside, newboard, newflags = makeMove(side, board, move[0],
                                                   move[1], flags, move[2])

            # Fan out next level of tree
            score, moveList, moveTree = alphabeta(newside, newboard, newflags,
                                                  depth - 1, alpha, beta)

            moves[encode(*move)] = score
            moveTrees[encode(*move)] = moveTree
            moveLists[encode(*move)] = moveList

            value = value if value > score else score
            alpha = alpha if alpha > value else value
            if (alpha >= beta):
                return score, [], moveTrees

    # Choose move based on side optimization
    if len(moves) > 0:
        if side:
            best_move = min(moves, key=moves.get)
        else:
            best_move = max(moves, key=moves.get)
    else:
        return evaluate(board), [], {}

    newMoveList = []
    if moveLists[best_move] != None and len(moveLists[best_move]) >= 0:

        newMoveList = [decode(best_move), *moveLists[best_move]]

    # print(moves)
    # print("Move tree", moveTrees)
    return moves[best_move], newMoveList, moveTrees
Ejemplo n.º 21
0
def stochastic(side, board, flags, depth, breadth, chooser):
    '''
    Choose the best move based on breadth randomly chosen paths per move, of length depth-1.
    Return: (value, moveList, moveTree)
      value (float): average board value of the paths for the best-scoring move
      moveLists (list): any sequence of moves, of length depth, starting with the best move
      moveTree (dict: encode(*move)->dict): a tree of moves that were evaluated in the search process
    Input:
      side (boolean): True if player1 (Min) plays next, otherwise False
      board (2-tuple of lists): current board layout, used by generateMoves and makeMove
      flags (list of flags): list of flags, used by generateMoves and makeMove
      depth (int >=0): depth of the search (number of moves)
      breadth: number of different paths
      chooser: a function similar to random.choice, but during autograding, might not be random.
    '''
    def findPath(side, board, flags, depth, chooser):
        # print(depth)
        if depth == 0:
            return evaluate(board), [], {}
        else:
            moves = []
            for move in generateMoves(side, board, flags):
                moves.append(move)

            if len(moves) == 0:
                return evaluate(board), [], {}

            chosen_move = chooser(moves)

            newside, newboard, newflags = makeMove(side, board, chosen_move[0],
                                                   chosen_move[1], flags,
                                                   chosen_move[2])

            score, path, tree = findPath(newside, newboard, newflags,
                                         depth - 1, chooser)
            # print(path, [chosen_move, *path])
            return score, [chosen_move, *path], {encode(*chosen_move): tree}

    potential_moves = {}

    # Iteratively looping through all depth level moves
    for move in generateMoves(side, board, flags):
        # print(move)
        # Make move
        newside, newboard, newflags = makeMove(side, board, move[0], move[1],
                                               flags, move[2])

        moves = []
        for child_move in generateMoves(newside, newboard, newflags):
            moves.append(child_move)

        if len(moves) > 0:
            # Investigate breadth moves and keep track of the best one
            total_score = 0
            best_move = None
            best_score = best_score = math.inf if side else -math.inf
            best_move_list = None
            move_trees = {}
            for i in range(breadth):
                chosen_move = chooser(moves)
                # print("Grandchildren: ", chosen_move)
                nextMoveSide, nextMoveBoard, nextMoveFlags = makeMove(
                    newside, newboard, chosen_move[0], chosen_move[1],
                    newflags, chosen_move[2])
                score, moveList, moveTree = findPath(nextMoveSide,
                                                     nextMoveBoard,
                                                     nextMoveFlags, depth - 2,
                                                     chooser)
                move_trees[encode(*chosen_move)] = moveTree
                if (side and score <= best_score) or (not (side)
                                                      and score >= best_score):
                    best_score = score
                    best_move = chosen_move
                    best_move_list = moveList

                    # print(moveList, best_score, score)
                total_score += score
            # print(best_move_list, best_move)
            # Calculate the average score
            avg_score = total_score / breadth
            potential_moves[encode(*move)] = (avg_score,
                                              [best_move,
                                               *best_move_list], move_trees)

        else:
            potential_moves[encode(*move)] = (evaluate(newboard), [], {})

    if (len(potential_moves) > 0):
        best_move = None
        best_move_list = None
        move_trees = {}
        best_score = math.inf if side else -math.inf
        for move in potential_moves:
            # print(move, potential_moves[move])
            move_trees[move] = potential_moves[move][2]
            if (side and potential_moves[move][0] <= best_score) or (
                    not (side) and potential_moves[move][0] >= best_score):
                # print(move, potential_moves[move])
                best_score = potential_moves[move][0]
                best_move_list = potential_moves[move][1]
                best_move = move
        # print([decode(best_move), *best_move_list], {best_move: best_move_tree})
        #print(best_score, [decode(best_move), *best_move_list], move_trees)
        return best_score, [decode(best_move), *best_move_list], move_trees
    else:
        return evaluate(board, [], {})