Exemple #1
0
def alphaBeta(board, depth, alpha=-MATE_VALUE, beta=MATE_VALUE, ply=0):
    """ This is a alphabeta/negamax/quiescent/iterativedeepend search algorithm
        Based on moves found by the validator.py findmoves2 function and
        evaluated by eval.py.
        The function recalls itself "depth" times. If the last move in range
        depth was a capture, it will continue calling itself, only searching for
        captures.
        It returns a tuple of
        *   a list of the path it found through the search tree (last item being
            the deepest)
        *   a score of your standing the the last possition. """

    global searching, nodes, table, endtime, timecheck_counter
    foundPv = False
    hashf = hashfALPHA
    amove = []

    ############################################################################
    # Mate distance pruning
    ############################################################################

    MATED = -MATE_VALUE + ply
    MATE_IN_1 = MATE_VALUE - ply - 1

    if beta <= MATED:
        return [], MATED
    if beta >= MATE_IN_1:
        beta = MATE_IN_1
        if alpha >= beta:
            return [], MATE_IN_1

    if board.variant == ATOMICCHESS:
        if bin(board.boards[board.color][KING]).count("1") == 0:
            return [], MATED
    elif board.variant == KINGOFTHEHILLCHESS:
        if testKingInCenter(board):
            return [], MATED
    elif board.variant == THREECHECKCHESS:
        if checkCount(board) == 3:
            return [], MATED

    ############################################################################
    # Look in the end game table
    ############################################################################

    global egtb
    if egtb:
        tbhits = egtb.scoreAllMoves(board)
        if tbhits:
            move, state, steps = tbhits[0]

            if state == DRAW:
                score = 0
            elif board.color == WHITE:
                if state == WHITEWON:
                    score = MATE_VALUE - steps
                else:
                    score = -MATE_VALUE + steps
            else:
                if state == WHITEWON:
                    score = -MATE_VALUE + steps
                else:
                    score = MATE_VALUE - steps
            return [move], score

    ###########################################################################
    # We don't save repetition in the table, so we need to test draw before   #
    # table.                                                                  #
    ###########################################################################

    # We don't adjudicate draws. Clients may have different rules for that.
    if ply > 0:
        if ldraw.test(board):
            return [], 0

    ############################################################################
    # Look up transposition table                                              #
    ############################################################################
    # TODO: add holder to hash
    if board.variant not in DROP_VARIANTS:
        if ply == 0:
            table.newSearch()

        table.setHashMove(depth, -1)
        probe = table.probe(board, depth, alpha, beta)
        if probe:
            move, score, hashf = probe
            score = VALUE_AT_PLY(score, ply)
            table.setHashMove(depth, move)

            if hashf == hashfEXACT:
                return [move], score
            elif hashf == hashfBETA:
                beta = min(score, beta)
            elif hashf == hashfALPHA:
                alpha = score

            if hashf != hashfBAD and alpha >= beta:
                return [move], score

    ############################################################################
    # Cheking the time                                                         #
    ############################################################################

    timecheck_counter -= 1
    if timecheck_counter == 0:
        if time() > endtime:
            searching = False
        timecheck_counter = TIMECHECK_FREQ

    ############################################################################
    # Break itereation if interupted or if times up                            #
    ############################################################################

    if not searching:
        return [], -evaluateComplete(board, 1 - board.color)

    ############################################################################
    # Go for quiescent search                                                  #
    ############################################################################

    isCheck = board.isChecked()

    if depth <= 0:
        if isCheck:
            # Being in check is that serious, that we want to take a deeper look
            depth += 1
        elif board.variant in (LOSERSCHESS, SUICIDECHESS, ATOMICCHESS):
            return [], evaluateComplete(board, board.color)
        else:
            mvs, val = quiescent(board, alpha, beta, ply)
            return mvs, val

    ############################################################################
    # Find and sort moves                                                      #
    ############################################################################

    if board.variant in (LOSERSCHESS, SUICIDECHESS):
        mlist = [m for m in genCaptures(board)]
        if board.variant == LOSERSCHESS and isCheck:
            evasions = [m for m in genCheckEvasions(board)]
            eva_cap = [m for m in evasions if m in mlist]
            mlist = eva_cap if eva_cap else evasions
        if not mlist and not isCheck:
            mlist = [m for m in genAllMoves(board)]
        moves = [(-getMoveValue(board, table, depth, m), m) for m in mlist]
    elif board.variant == ATOMICCHESS:
        if isCheck:
            mlist = [m
                     for m in genCheckEvasions(board)
                     if not kingExplode(board, m, board.color)]
        else:
            mlist = [m
                     for m in genAllMoves(board)
                     if not kingExplode(board, m, board.color)]
        moves = [(-getMoveValue(board, table, depth, m), m) for m in mlist]
    else:
        if isCheck:
            moves = [(-getMoveValue(board, table, depth, m), m)
                     for m in genCheckEvasions(board)]
        else:
            moves = [(-getMoveValue(board, table, depth, m), m)
                     for m in genAllMoves(board)]
    moves.sort()

    # This is needed on checkmate
    catchFailLow = None

    ############################################################################
    # Loop moves                                                               #
    ############################################################################

    for moveValue, move in moves:

        nodes += 1

        board.applyMove(move)
        if not isCheck:
            if board.opIsChecked():
                board.popMove()
                continue

        catchFailLow = move

        if foundPv:
            mvs, val = alphaBeta(board, depth - 1, -alpha - 1, -alpha, ply + 1)
            val = -val
            if val > alpha and val < beta:
                mvs, val = alphaBeta(board, depth - 1, -beta, -alpha, ply + 1)
                val = -val
        else:
            mvs, val = alphaBeta(board, depth - 1, -beta, -alpha, ply + 1)
            val = -val

        board.popMove()

        if val > alpha:
            if val >= beta:
                if searching and move >> 12 != DROP:
                    table.record(board, move, VALUE_AT_PLY(beta, -ply),
                                 hashfBETA, depth)
                    # We don't want to use our valuable killer move spaces for
                    # captures and promotions, as these are searched early anyways.
                    if board.arBoard[move & 63] == EMPTY and \
                            not move >> 12 in PROMOTIONS:
                        table.addKiller(depth, move)
                        table.addButterfly(move, depth)
                return [move] + mvs, beta

            alpha = val
            amove = [move] + mvs
            hashf = hashfEXACT
            foundPv = True

    ############################################################################
    # Return                                                                   #
    ############################################################################

    if amove:
        if searching:
            table.record(board, amove[0], VALUE_AT_PLY(alpha, -ply), hashf,
                         depth)
            if board.arBoard[amove[0] & 63] == EMPTY:
                table.addKiller(depth, amove[0])
        return amove, alpha

    if catchFailLow:
        if searching:
            table.record(board, catchFailLow, VALUE_AT_PLY(alpha, -ply), hashf,
                         depth)
        return [catchFailLow], alpha

    # If no moves were found, this must be a mate or stalemate
    if isCheck:
        return [], MATED

    return [], 0
Exemple #2
0
def getStatus(board):
    lboard = board.board

    if board.variant == LOSERSCHESS:
        if testKingOnly(lboard):
            if board.color == WHITE:
                status = WHITEWON
            else:
                status = BLACKWON
            return status, WON_NOMATERIAL
    elif board.variant == SUICIDECHESS or board.variant == GIVEAWAYCHESS:
        if pieceCount(lboard, lboard.color) == 0:
            if board.color == WHITE:
                status = WHITEWON
            else:
                status = BLACKWON
            return status, WON_NOMATERIAL
    elif board.variant == HORDECHESS:
        if pieceCount(lboard, lboard.color) == 0 and board.color == WHITE:
            status = BLACKWON
            return status, WON_WIPEOUT
    elif board.variant == ATOMICCHESS:
        if lboard.boards[board.color][KING] == 0:
            if board.color == WHITE:
                status = BLACKWON
            else:
                status = WHITEWON
            return status, WON_KINGEXPLODE
    elif board.variant == KINGOFTHEHILLCHESS:
        if testKingInCenter(lboard):
            if board.color == BLACK:
                status = WHITEWON
            else:
                status = BLACKWON
            return status, WON_KINGINCENTER
    elif board.variant == THREECHECKCHESS:
        if checkCount(lboard) == 3:
            if board.color == BLACK:
                status = WHITEWON
            else:
                status = BLACKWON
            return status, WON_THREECHECK
    elif board.variant == RACINGKINGSCHESS:
        if testKingInEightRow(lboard):
            if board.color == BLACK:
                status = WHITEWON
            else:
                status = BLACKWON
            return status, WON_KINGINEIGHTROW
    else:
        if ldraw.testMaterial(lboard):
            return DRAW, DRAW_INSUFFICIENT

    hasMove = False
    for move in lmovegen.genAllMoves(lboard):
        if board.variant == ATOMICCHESS:
            if kingExplode(lboard, move, 1 - board.color) and not kingExplode(
                    lboard, move, board.color):
                hasMove = True
                break
            elif kingExplode(lboard, move, board.color):
                continue
        lboard.applyMove(move)
        if lboard.opIsChecked():
            lboard.popMove()
            continue
        hasMove = True
        lboard.popMove()
        break

    if not hasMove:
        if lboard.isChecked():
            if board.variant == LOSERSCHESS:
                if board.color == WHITE:
                    status = WHITEWON
                else:
                    status = BLACKWON
            else:
                if board.color == WHITE:
                    status = BLACKWON
                else:
                    status = WHITEWON
            return status, WON_MATE
        else:
            if board.variant == LOSERSCHESS or board.variant == GIVEAWAYCHESS:
                if board.color == WHITE:
                    status = WHITEWON
                else:
                    status = BLACKWON
                return status, DRAW_STALEMATE
            elif board.variant == SUICIDECHESS:
                if pieceCount(lboard, WHITE) == pieceCount(lboard, BLACK):
                    return status, DRAW_EQUALMATERIAL
                else:
                    if board.color == WHITE and pieceCount(
                            lboard, WHITE) < pieceCount(lboard, BLACK):
                        status = WHITEWON
                    else:
                        status = BLACKWON
                    return status, WON_LESSMATERIAL
            else:
                return DRAW, DRAW_STALEMATE

    if lboard.repetitionCount() >= 3:
        return DRAW, DRAW_REPITITION

    if ldraw.testFifty(lboard):
        return DRAW, DRAW_50MOVES

    return RUNNING, UNKNOWN_REASON
Exemple #3
0
def alphaBeta(board, depth, alpha=-MATE_VALUE, beta=MATE_VALUE, ply=0):
    """ This is a alphabeta/negamax/quiescent/iterativedeepend search algorithm
        Based on moves found by the validator.py findmoves2 function and
        evaluated by eval.py.
        
        The function recalls itself "depth" times. If the last move in range
        depth was a capture, it will continue calling itself, only searching for
        captures.
        
        It returns a tuple of
        *   a list of the path it found through the search tree (last item being
            the deepest)
        *   a score of your standing the the last possition. """

    global searching, nodes, table, endtime, timecheck_counter
    foundPv = False
    hashf = hashfALPHA
    amove = []

    ############################################################################
    # Mate distance pruning
    ############################################################################

    MATED = -MATE_VALUE + ply
    MATE_IN_1 = MATE_VALUE - ply - 1

    if beta <= MATED:
        return [], MATED
    if beta >= MATE_IN_1:
        beta = MATE_IN_1
        if alpha >= beta:
            return [], MATE_IN_1

    if board.variant == ATOMICCHESS:
        if bin(board.boards[board.color][KING]).count("1") == 0:
            return [], MATED
    elif board.variant == KINGOFTHEHILLCHESS:
        if testKingInCenter(board):
            return [], MATED
    elif board.variant == THREECHECKCHESS:
        if checkCount(board) == 3:
            return [], MATED

    ############################################################################
    # Look in the end game table
    ############################################################################

    global egtb
    if egtb:
        tbhits = egtb.scoreAllMoves(board)
        if tbhits:
            move, state, steps = tbhits[0]

            if state == DRAW:
                score = 0
            elif board.color == WHITE:
                if state == WHITEWON:
                    score = MATE_VALUE - steps
                else:
                    score = -MATE_VALUE + steps
            else:
                if state == WHITEWON:
                    score = -MATE_VALUE + steps
                else:
                    score = MATE_VALUE - steps
            return [move], score

    ###########################################################################
    # We don't save repetition in the table, so we need to test draw before   #
    # table.                                                                  #
    ###########################################################################

    # We don't adjudicate draws. Clients may have different rules for that.
    if ply > 0:
        if ldraw.test(board):
            return [], 0

    ############################################################################
    # Look up transposition table                                              #
    ############################################################################
    # TODO: add holder to hash
    if board.variant not in DROP_VARIANTS:
        if ply == 0:
            table.newSearch()

        table.setHashMove(depth, -1)
        probe = table.probe(board, depth, alpha, beta)
        hashmove = None
        if probe:
            move, score, hashf = probe
            score = VALUE_AT_PLY(score, ply)
            hashmove = move
            table.setHashMove(depth, move)

            if hashf == hashfEXACT:
                return [move], score
            elif hashf == hashfBETA:
                beta = min(score, beta)
            elif hashf == hashfALPHA:
                alpha = score

            if hashf != hashfBAD and alpha >= beta:
                return [move], score

    ############################################################################
    # Cheking the time                                                         #
    ############################################################################

    timecheck_counter -= 1
    if timecheck_counter == 0:
        if time() > endtime:
            searching = False
        timecheck_counter = TIMECHECK_FREQ

    ############################################################################
    # Break itereation if interupted or if times up                            #
    ############################################################################

    if not searching:
        return [], -evaluateComplete(board, 1 - board.color)

    ############################################################################
    # Go for quiescent search                                                  #
    ############################################################################

    isCheck = board.isChecked()

    if depth <= 0:
        if isCheck:
            # Being in check is that serious, that we want to take a deeper look
            depth += 1
        elif board.variant in (LOSERSCHESS, SUICIDECHESS, ATOMICCHESS):
            return [], evaluateComplete(board, board.color)
        else:
            mvs, val = quiescent(board, alpha, beta, ply)
            return mvs, val

    ############################################################################
    # Find and sort moves                                                      #
    ############################################################################

    if board.variant in (LOSERSCHESS, SUICIDECHESS):
        mlist = [m for m in genCaptures(board)]
        if board.variant == LOSERSCHESS and isCheck:
            evasions = [m for m in genCheckEvasions(board)]
            eva_cap = [m for m in evasions if m in mlist]
            mlist = eva_cap if eva_cap else evasions
        if not mlist and not isCheck:
            mlist = [m for m in genAllMoves(board)]
        moves = [(-getMoveValue(board, table, depth, m), m) for m in mlist]
    elif board.variant == ATOMICCHESS:
        if isCheck:
            mlist = [
                m for m in genCheckEvasions(board)
                if not kingExplode(board, m, board.color)
            ]
        else:
            mlist = [
                m for m in genAllMoves(board)
                if not kingExplode(board, m, board.color)
            ]
        moves = [(-getMoveValue(board, table, depth, m), m) for m in mlist]
    else:
        if isCheck:
            moves = [(-getMoveValue(board, table, depth, m), m)
                     for m in genCheckEvasions(board)]
        else:
            moves = [(-getMoveValue(board, table, depth, m), m)
                     for m in genAllMoves(board)]
    moves.sort()

    # This is needed on checkmate
    catchFailLow = None

    ############################################################################
    # Loop moves                                                               #
    ############################################################################

    for moveValue, move in moves:

        nodes += 1

        board.applyMove(move)
        if not isCheck:
            if board.opIsChecked():
                board.popMove()
                continue

        catchFailLow = move

        if foundPv:
            mvs, val = alphaBeta(board, depth - 1, -alpha - 1, -alpha, ply + 1)
            val = -val
            if val > alpha and val < beta:
                mvs, val = alphaBeta(board, depth - 1, -beta, -alpha, ply + 1)
                val = -val
        else:
            mvs, val = alphaBeta(board, depth - 1, -beta, -alpha, ply + 1)
            val = -val

        board.popMove()

        if val > alpha:
            if val >= beta:
                if searching and move >> 12 != DROP:
                    table.record(board, move, VALUE_AT_PLY(beta, -ply),
                                 hashfBETA, depth)
                    # We don't want to use our valuable killer move spaces for
                    # captures and promotions, as these are searched early anyways.
                    if board.arBoard[move&63] == EMPTY and \
                            not move>>12 in PROMOTIONS:
                        table.addKiller(depth, move)
                        table.addButterfly(move, depth)
                return [move] + mvs, beta

            alpha = val
            amove = [move] + mvs
            hashf = hashfEXACT
            foundPv = True

    ############################################################################
    # Return                                                                   #
    ############################################################################

    if amove:
        if searching:
            table.record(board, amove[0], VALUE_AT_PLY(alpha, -ply), hashf,
                         depth)
            if board.arBoard[amove[0] & 63] == EMPTY:
                table.addKiller(depth, amove[0])
        return amove, alpha

    if catchFailLow:
        if searching:
            table.record(board, catchFailLow, VALUE_AT_PLY(alpha, -ply), hashf,
                         depth)
        return [catchFailLow], alpha

    # If no moves were found, this must be a mate or stalemate
    if isCheck:
        return [], MATED

    return [], 0