Exemplo n.º 1
0
def parseFAN (board, fan):
    """ Parse a Long/Expanded Algebraic Notation string """
    
    san = fan2SanRegex.sub(fan2SanFunc, fan)
    
    pawnFan = FAN_PIECES[board.color][PAWN]
    if san[0] == pawnFan:
        san = san.replace(pawnFan, "")
        # If the pawn file has been omitted from a capture fan notation, it
        # means that there was only one pawn able to move to the end cord. We
        # just need to find it.
        if san[0] == "x":
            # We need to find the endcord ourselves. Can't wait for parseSAN
            i = san.find("=")
            if i >= 0:
                tocord = san[1:i]
            else: tocord = san[1:]
            tcord = cordDic[tocord]
            
            from lmovegen import genAllMoves
            board_clone = board.clone()
            for altmove in genAllMoves(board_clone):
                if board_clone.arBoard[FCORD(altmove)] == PAWN and \
                        TCORD(altmove) == tcord:
                    board_clone.applyMove(altmove)
                    if not board_clone.opIsChecked():
                        san = reprFile(mfcord) + san
                    board_clone.popMove()
                    # We know there is only one pawn which can move to tcord, so
                    # we stop work here
                    break
    
    return parseSAN (board, san)
Exemplo n.º 2
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 last, searching, nodes, movesearches, table, endtime, timecheck_counter
    foundPv = False
    hashf = hashfALPHA
    amove = []

    ############################################################################
    # Look up transposition table                                              #
    ############################################################################
    
    table.setHashMove (ply, -1)
    probe = table.probe (board.hash, ply, alpha, beta)
    
    if probe:
        move, score, hashf = probe
        table.setHashMove (ply, move)
        
        if hashf == hashfEXACT:
            return [move], score
        elif hashf == hashfBETA:
            beta = min(score, beta)
        elif hashf == hashfALPHA:
            alpha = score
            
        if alpha >= beta:
            return [move], score
    
    if ldraw.test(board):
        return [], 0
    
    ############################################################################
    # 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:
        last = 1
        return [], evaluateComplete(board, 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
        else:
            last = 0
            return quiescent(board, alpha, beta, ply)
    
    ############################################################################
    # Find and sort moves                                                      #
    ############################################################################
    
    movesearches += 1
    
    # TODO: Using heap is slower than simply doing a list.sort()
    
    heap = []
    if isCheck:
        for move in genCheckEvasions(board):
            heappush(heap, (-getMoveValue (board, table, ply, move), move))
    else:
        for move in genAllMoves(board):
            heappush(heap, (-getMoveValue (board, table, ply, move), move))
    
    # This is needed on checkmate
    catchFailLow = None
    
    ############################################################################
    # Loop moves                                                               #
    ############################################################################
    
    while heap:
        nodes += 1
        
        v, move = heappop(heap)
        
        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:
                table.record (board.hash, move, beta, hashfBETA, ply)
                # 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 (ply, move)
                last = 2
                return [move]+mvs, beta
                
            alpha = val
            amove = [move]+mvs
            hashf = hashfEXACT
            foundPv = True
    
    ############################################################################
    # Return                                                                   #
    ############################################################################
    
    if amove:
        last = 3
        table.record (board, amove[0], alpha, hashf, ply)
        if board.arBoard[amove[0]&63] == EMPTY:
            table.addKiller (ply, amove[0])
        return amove, alpha
        
    if catchFailLow:
        last = 4
        return [catchFailLow], alpha

    # If no moves were found, this must be a mate or stalemate
    last = 5
    if isCheck:
        return [], -MATE_VALUE+ply-2
    
    last = 6
    return [], 0
Exemplo n.º 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 last, searching, nodes, movesearches, table, endtime, timecheck_counter
    foundPv = False
    hashf = hashfALPHA
    amove = []
    
    ############################################################################
    # Look in the end game table
    ############################################################################
    
    if useegtb:
        egtb = probeEndGameTable(board)
        if egtb:
            move, state, steps = egtb[0]
            
            if state == DRAW:
                score = 0
            elif board.color == WHITE:
                if state == WHITEWON:
                    score = MATE_VALUE-steps+2
                else: score = -MATE_VALUE+steps-2
            else:
                if state == WHITEWON:
                    score = -MATE_VALUE+steps-2
                else: score = MATE_VALUE-steps+2
            last = 1
            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):
            last = 2
            return [], 0
    
    ############################################################################
    # Look up transposition table                                              #
    ############################################################################
    
    table.setHashMove (depth, -1)
    probe = table.probe (board, depth, alpha, beta)
    hashmove = None
    if probe:
        move, score, hashf = probe
        hashmove = move
        table.setHashMove (depth, move)
        
        if hashf == hashfEXACT:
            last = 3
            return [move], score
        elif hashf == hashfBETA:
            beta = min(score, beta)
        elif hashf == hashfALPHA:
            alpha = score
            
        if hashf != hashfBAD and alpha >= beta:
            last = 4
            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:
        last = 5
        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
        else:
            last = 6
            mvs, val = quiescent(board, alpha, beta, ply)
            return mvs, val
    
    ############################################################################
    # Find and sort moves                                                      #
    ############################################################################
    
    movesearches += 1
    
    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:
                    table.record (board, move, beta, 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)
                last = 7
                return [move]+mvs, beta
            
            alpha = val
            amove = [move]+mvs
            hashf = hashfEXACT
            foundPv = True
    
    ############################################################################
    # Return                                                                   #
    ############################################################################
    
    if amove:
        if searching:
            table.record (board, amove[0], alpha, hashf, depth)
            if board.arBoard[amove[0]&63] == EMPTY:
                table.addKiller (depth, amove[0])
        last = 8
        return amove, alpha
    
    if catchFailLow:
        if searching:
            table.record (board, catchFailLow, alpha, hashf, depth)
        last = 9
        return [catchFailLow], alpha

    # If no moves were found, this must be a mate or stalemate
    if isCheck:
        last = 10
        return [], -MATE_VALUE+ply-2
    
    last = 11
    return [], 0
Exemplo n.º 4
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 last, searching, nodes, movesearches, table, endtime, timecheck_counter
    foundPv = False
    hashf = hashfALPHA
    amove = []

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

    if useegtb:
        egtb = probeEndGameTable(board)
        if egtb:
            move, state, steps = egtb[0]

            if state == DRAW:
                score = 0
            elif board.color == WHITE:
                if state == WHITEWON:
                    score = MATE_VALUE - steps + 2
                else:
                    score = -MATE_VALUE + steps - 2
            else:
                if state == WHITEWON:
                    score = -MATE_VALUE + steps - 2
                else:
                    score = MATE_VALUE - steps + 2
            last = 1
            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):
            last = 2
            return [], 0

    ############################################################################
    # Look up transposition table                                              #
    ############################################################################

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

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

        if hashf != hashfBAD and alpha >= beta:
            last = 4
            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:
        last = 5
        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
        else:
            last = 6
            mvs, val = quiescent(board, alpha, beta, ply)
            return mvs, val

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

    movesearches += 1

    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:
                    table.record(board, move, beta, 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)
                last = 7
                return [move] + mvs, beta

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

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

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

    if catchFailLow:
        if searching:
            table.record(board, catchFailLow, alpha, hashf, depth)
        last = 9
        return [catchFailLow], alpha

    # If no moves were found, this must be a mate or stalemate
    if isCheck:
        last = 10
        return [], -MATE_VALUE + ply - 2

    last = 11
    return [], 0
Exemplo n.º 5
0
def parseSAN (board, san):
    """ Parse a Short/Abbreviated Algebraic Notation string """
    if not san:
        raise ParsingError, (san, _("the move is an empty string"), board.asFen())
    elif len(san) < 2:
        raise ParsingError, (san, _("the move is too short"), board.asFen())
    
    notat = san
    
    if notat[-1] in ("+", "#"):
        notat = notat[:-1]
    
    flag = NORMAL_MOVE
    
    # If last char is a piece char, we assue it the promote char
    c = notat[-1].lower()
    if c in chr2Sign:
        flag = chr2Sign[c] + 2
        if notat[-2] == "=":
            notat = notat[:-2]
        else: notat = notat[:-1]
    
    if len(notat) < 2:
        raise ParsingError, (san, _("the move needs a piece and a cord"), board.asFen())
    
    notat = notat.replace("0","O").replace("o","O")
    if notat.startswith("O-O"):
        if board.color == WHITE:
            fcord = board.ini_kings[0] #E1
            if notat == "O-O":
                flag = KING_CASTLE
                if board.variant == FISCHERRANDOMCHESS:
                    tcord = board.ini_rooks[0][1]
                else:
                    tcord = G1
            else:
                flag = QUEEN_CASTLE
                if board.variant == FISCHERRANDOMCHESS:
                    tcord = board.ini_rooks[0][0]
                else:
                    tcord = C1
        else:
            fcord = board.ini_kings[1] #E8
            if notat == "O-O":
                flag = KING_CASTLE
                if board.variant == FISCHERRANDOMCHESS:
                    tcord = board.ini_rooks[1][1]
                else:
                    tcord = G8
            else:
                flag = QUEEN_CASTLE
                if board.variant == FISCHERRANDOMCHESS:
                    tcord = board.ini_rooks[1][0]
                else:
                    tcord = C8
        
        return newMove (fcord, tcord, flag)
    
    if notat[0] in ("Q", "R", "B", "K", "N"):
        piece = chr2Sign[notat[0].lower()]
        notat = notat[1:]
    else:
        piece = PAWN
    
    if "x" in notat:
        notat, tcord = notat.split("x")
        if not tcord in cordDic:
            raise ParsingError, (
                    san, _("the captured cord (%s) is incorrect") % tcord, board.asFen())
        
        tcord = cordDic[tcord]
        if piece == PAWN:
            # If a pawn is attacking an empty cord, we assue it an enpassant
            if board.arBoard[tcord] == EMPTY:
                flag = ENPASSANT
    else:
        if not notat[-2:] in cordDic:
            raise ParsingError, (
                    san, "the end cord (%s) is incorrect" % notat[-2:], board.asFen())
        
        tcord = cordDic[notat[-2:]]
        notat = notat[:-2]
    
    # If there is any extra location info, like in the move Bexd1 or Nh3f4 we
    # want to know
    frank = None
    ffile = None
    if notat and notat[0] in reprRank:
        frank = int(notat[0])-1
        notat = notat[1:]
    if notat and notat[0] in reprFile:
        ffile = ord(notat[0]) - ord("a")
        notat = notat[1:]
    if notat and notat[0] in reprRank:
        frank = int(notat[0])-1
        notat = notat[1:]
    
    # We find all pieces who could have done it. (If san was legal, there should
    # never be more than one)
    from lmovegen import genAllMoves
    for move in genAllMoves(board):
        if TCORD(move) != tcord:
            continue
        f = FCORD(move)
        if board.arBoard[f] != piece:
            continue
        if frank != None and frank != RANK(f):
            continue
        if ffile != None and ffile != FILE(f):
            continue
        if flag in PROMOTIONS and FLAG(move) != flag:
            continue
        
        board_clone = board.clone()
        board_clone.applyMove(move)
        if board_clone.opIsChecked():
            continue
        
        return move
    
    errstring = "no %s is able to move to %s" % (reprPiece[piece], reprCord[tcord])
    raise ParsingError, (san, errstring, board.asFen())
Exemplo n.º 6
0
def toSAN (board, move, localRepr=False):
    """ Returns a Short/Abbreviated Algebraic Notation string of a move 
        The board should be prior to the move """
    
    # Has to be importet at calltime, as lmovegen imports lmove
    from lmovegen import genAllMoves
    
    flag = move >> 12
    
    if flag == KING_CASTLE:
        return "O-O"
    elif flag == QUEEN_CASTLE:
        return "O-O-O"
    
    fcord = (move >> 6) & 63
    tcord = move & 63
    
    fpiece = board.arBoard[fcord]
    tpiece = board.arBoard[tcord]
    
    part0 = ""
    part1 = ""
    
    if fpiece != PAWN:
        if localRepr:
            part0 += localReprSign[fpiece]
        else:
            part0 += reprSign[fpiece]
    
    part1 = reprCord[tcord]
    
    if not fpiece in (PAWN, KING):
        xs = []
        ys = []
        
        board_clone = board.clone()
        for altmove in genAllMoves(board_clone):
            mfcord = FCORD(altmove)
            if board_clone.arBoard[mfcord] == fpiece and \
                    mfcord != fcord and \
                    TCORD(altmove) == tcord:
                board_clone.applyMove(altmove)
                if not board_clone.opIsChecked():
                    xs.append(FILE(mfcord))
                    ys.append(RANK(mfcord))
                board_clone.popMove()

        x = FILE(fcord)
        y = RANK(fcord)
        
        if ys or xs:
            if y in ys and not x in xs:
                # If we share rank with another piece, but not file
                part0 += reprFile[x]
            elif x in xs and not y in ys:
                # If we share file with another piece, but not rank
                part0 += reprRank[y]
            elif x in xs and y in ys:
                # If we share both file and rank with other pieces
                part0 += reprFile[x] + reprRank[y]
            else:
                # If we doesn't share anything, it is standard to put file
                part0 += reprFile[x]
    
    if tpiece != EMPTY or flag == ENPASSANT:
        part1 = "x" + part1
        if fpiece == PAWN:
            part0 += reprFile[FILE(fcord)]
    
    notat = part0 + part1
    if flag in PROMOTIONS:
        if localRepr:
            notat += "="+localReprSign[PROMOTE_PIECE(flag)]
        else:
            notat += "="+reprSign[PROMOTE_PIECE(flag)]
    
    board_clone = board.clone()
    board_clone.applyMove(move)
    if board_clone.isChecked():
        for altmove in genAllMoves (board_clone):
            board_clone.applyMove(altmove)
            if board_clone.opIsChecked():
                board_clone.popMove()
                continue
            notat += "+"
            break
        else:
            notat += "#"
    
    return notat
Exemplo n.º 7
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

    ############################################################################
    # 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.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 != CRAZYHOUSECHESS:
        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