Esempio n. 1
0
BOOL is_checkmate(struct t_board *board)
{
    if (!board->in_check)
        return FALSE;

    struct t_move_list moves[1];
    generate_evade_check(board, moves);

    if (moves->count > 0)
        return FALSE;

    return TRUE;
}
Esempio n. 2
0
t_nodes do_perft(struct t_board *board, int depth)
{
    struct t_move_list move_list[1];
	struct t_move_list bad_move_list[1];
	bad_move_list->count = 0;
	bad_move_list->imove = 0;
    struct t_undo undo[1];

    t_nodes nodes = 0;
    int i;

    assert(integrity(board));
    if (board->in_check) {
        generate_evade_check(board, move_list);
        if (depth == 1) return move_list->count;
    }
    else {

        //generate_captures(board, move_list);
        //generate_quiet_moves(board, move_list);
        generate_moves(board, move_list);
        //if (!equal_move_lists(move_list, xmove_list)){
        //	write_board(board, "board.txt");
        //	write_move_list(xmove_list, "all.txt");
        //	write_move_list(move_list, "inc.txt");
        //}
        if (depth == 1) return legal_move_count(board, move_list);
    }

    for (i = move_list->count - 1; i >= 0; i--) {
        assert(lookup_move(board, move_as_str(move_list->move[i])) == move_list->move[i]);
        if (make_next_move(board, move_list, bad_move_list, undo)) {
            assert(integrity(board));

            //nodes++;
            if (depth > 1)
                nodes += do_perft(board, depth - 1);
            else
                nodes++;
            unmake_move(board, undo);
            assert(integrity(board));
        }
        else
            assert(integrity(board));
    }

    return nodes;

}
Esempio n. 3
0
t_nodes perft(struct t_board *board, int depth) {

    struct t_move_list move_list[1];
    struct t_undo undo[1];

    t_nodes total_nodes = 0;
    t_nodes move_nodes = 0;

    unsigned long start = time_now();

    int i;

    if (board->in_check)
        generate_evade_check(board, move_list);
    else
        generate_moves(board, move_list);

    for (i = move_list->count - 1; i >= 0; i--) {
        if (make_move(board, move_list->pinned_pieces, move_list->move[i], undo)) {
            move_nodes = 0;
            if (depth > 1)
                move_nodes += do_perft(board, depth - 1);
            printf(move_as_str(move_list->move[i]));
            printf(" = %u\n", move_nodes);
            unmake_move(board, undo);
            total_nodes += move_nodes;
        }
    }

    unsigned long finish = time_now();

    if (finish == start)
        printf("Total Nodes: %I64d\n", total_nodes);
    else
        printf("Total Nodes: %I64d in %d milliseconds = nps %I64d\n", total_nodes, finish - start, 1000 * total_nodes / (finish - start));

    return total_nodes;
}
Esempio n. 4
0
BOOL test_genmove() {

    BOOL ok = TRUE;

    struct t_move_list moves[1];

    set_fen(position, "r5r1/n1q1pP1k/3pPppp/P1pP4/2P4N/R1B5/2Q3PP/7K w - -");
    assert(integrity(position));
    assert(is_square_attacked(position, E4, WHITE));
    assert(!is_square_attacked(position, A7, WHITE));
    assert(!is_square_attacked(position, F4, BLACK));
    assert(is_square_attacked(position, D8, BLACK));
    generate_moves(position, moves);
    ok = ok && (moves->count == 42);

    flip_board(position);
    generate_moves(position, moves);
    //write_move_list(moves, "movelist.txt");
    ok = ok && (moves->count == 42);

    set_fen(position, "1r2k2r/p1ppqpb1/b3pnp1/3PN3/1pn1P3/2N2Q1p/PPPBBPPP/R4K1R w - -");
    assert(integrity(position));
    generate_moves(position, moves);
    assert(move_list_integrity(position, moves));
    ok = ok && (moves->count == 44);

    set_fen(position, "4q3/3P1P2/b4N2/8/3Q2Bb/2p3B1/1k4N1/4K1Nr w - -");
    assert(integrity(position));
    generate_evade_check(position, moves);
    ok = ok && (moves->count == 18);

    flip_board(position);
    generate_evade_check(position, moves);
    ok = ok && (moves->count == 18);

    set_fen(position, "1r2k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R4K1R w --k- -");
    assert(integrity(position));
    generate_moves(position, moves);
    //write_move_list(moves, "movelist.txt");
    assert(move_list_integrity(position, moves));
    ok = ok && (moves->count == 46);

    flip_board(position);
    generate_moves(position, moves);
    ok = ok && (moves->count == 46);

	// Chess960 Examples
	set_fen(position, "Rr4kr/8/8/8/8/8/PPPP4/R1K5 w Ahb -");
	assert(integrity(position));
	generate_moves(position, moves);
	assert(move_list_integrity(position, moves));
	ok = ok && (moves->count == 18);

	flip_board(position);
	generate_moves(position, moves);
	ok = ok && (moves->count == 18);

	set_fen(position, "1r1kbb1r/1pp2ppp/3npn2/3pN3/1Q3P2/4PN2/2PP2PP/qR1KBB1R w HBhb -");
	assert(integrity(position));
	generate_legal_moves(position, moves);
	assert(move_list_integrity(position, moves));
	ok = ok && (moves->count == 48);

	set_fen(position, "rkrbqnb1/pp2p2p/3p1pp1/2p1nP2/2P1P3/3P2N1/PP4PP/RKRBQNB1 w CAca -");
	assert(integrity(position));
	generate_legal_moves(position, moves);
	write_move_list(moves, "movelist.txt");
	assert(move_list_integrity(position, moves));
	ok = ok && (moves->count == 34);

	flip_board(position);
	generate_legal_moves(position, moves);
	ok = ok && (moves->count == 34);


    return ok;
}
Esempio n. 5
0
t_chess_value qsearch(struct t_board *board, int ply, int depth, t_chess_value alpha, t_chess_value beta) {

    //-- Principle Variation
    struct t_pv_data *pv = &(board->pv_data[ply]);

    //-- Has the maximum depth been reached
    if (ply >= MAXPLY || uci.stop)
        return pv->eval->static_score;

    //-- Increment the node count
    qnodes++;

    //-- Is this the deepest?
    if (ply > deepest) {
        deepest = ply;
        do_uci_depth();
    }

    //-- Mate Distance Pruning
    if (CHECKMATE - ply <= alpha) {
        return alpha;
    }
    else if (-CHECKMATE + ply >= beta) {
        return beta;
    }

    //-- PV of Next Ply
    struct t_pv_data *next_pv = &(board->pv_data[ply + 1]);

    //-- Define the local variables
    pv->legal_moves_played = 0;
    t_chess_value best_score;
    t_chess_value a = alpha;
    t_chess_value b = beta;
    t_chess_value e;


    // Declare local variables
    t_undo undo[1];

    //-- Generate All Moves
    struct t_move_list moves[1];
    moves->hash_move = NULL;

    //-----------------------------------------------
    //-- Handling In-Check (e.g. Cannot Stand-Pat)
    //-----------------------------------------------
    if (board->in_check) {

        best_score = -CHECKMATE;

        //-- Generate moves which get out of check
        generate_evade_check(board, moves);

        // Are we in checkmate?
        if (moves->count == 0) {
            pv->best_line_length = ply;
            return -CHECKMATE + ply;
        }

        //-- Order the moves
        order_evade_check(board, moves, ply);

        //-- Play moves
        while (make_next_best_move(board, moves, undo)) {

            //-- Increment the "legal_moves_played" counter
            pv->legal_moves_played++;
            pv->current_move = moves->current_move;

            //-- Evaluate the new board position
            evaluate(board, next_pv->eval);

            //-- Search the next ply at reduced depth
            e = -qsearch(board, ply + 1, depth - 1, -b - 1, -a);

            //-- Is a research required?
            if (alpha + 1 != beta && e > a && a + 1 == b)
                e = -qsearch(board, ply + 1, depth - 1, -beta, -a);

            unmake_move(board, undo);

            //-- Is it good enough to cut-off?
            if (e >= beta) {
                update_check_killers(pv, 0);
                return e;
            }

            //-- Is it the best so far?
            if (e > best_score) {
                best_score = e;

                //-- Does it improve upon alpha (i.e. is it part of the PV)?
                if (e > a) {
                    a = e;

                    //-- Update the Principle Variation
                    update_best_line(board, ply);
                }
            }

            // Reset the zero width window
            b = a + 1;

        }

    }
    else {
        //--------------------------------------------------------
        //-- Normal moves handled differently (e.g. can stand-pat)
        //--------------------------------------------------------

        //-- Does stand-pat cause a cutoff?
        e = pv->eval->static_score;
        if (e >= beta)
            return e;

        //-- Does the current value raise alpha?
        best_score = e;
        if (e > alpha) {
            a = e;
            pv->best_line_length = ply;
        }

        //-- Generate all captures
        generate_captures(board, moves);

        //-- Order the moves
        order_captures(board, moves);

		//-- Only search break even captures during the PV
		//int threshold = 10;
		//if (beta != alpha + 1)
		int threshold = 0;

        //-- Play moves
        while (make_next_see_positive_move(board, moves, threshold, undo)) {

            //-- Increment the "legal_moves_played" counter
            pv->legal_moves_played++;
            pv->current_move = moves->current_move;

            //-- Evaluate the new board position
            evaluate(board, next_pv->eval);

            //-- Search the next ply at reduced depth
            e = -qsearch(board, ply + 1, depth - 1, -b, -a);

            //-- Is a research required?
            if (alpha + 1 != beta && e > a && a + 1 == b)
                e = -qsearch(board, ply + 1, depth - 1, -beta, -a);

            unmake_move(board, undo);

            //-- Is it good enough to cut-off?
            if (e >= beta)
                return e;

            //-- Is it the best so far?
            if (e > best_score) {
                best_score = e;

                //-- Does it improve upon alpha (i.e. is it part of the PV)?
                if (e > a) {
                    a = e;
                    update_best_line(board, ply);
                }
            }

            // Reset the zero width window
            b = a + 1;

        }
    }

    // Return Best Score found
    return best_score;

}
Esempio n. 6
0
t_chess_value qsearch_plus(struct t_board *board, int ply, int depth, t_chess_value alpha, t_chess_value beta) {

    //-- Principle Variation
    struct t_pv_data *pv = &(board->pv_data[ply]);

    //-- Has the maximum depth been reached
    if (ply >= MAXPLY || uci.stop)
        return pv->eval->static_score;

    //-- Increment the node count
    qnodes++;

    /* check to see if this is a repeated position or draw by 50 moves */
    if (repetition_draw(board)) {
        pv->best_line_length = ply;
        return 0;
    }

    //-- Mate Distance Pruning
    if (CHECKMATE - ply <= alpha) {
        return alpha;
    }
    else if (-CHECKMATE + ply >= beta) {
        return beta;
    }
    else if ((!board->in_check) && (-CHECKMATE + ply + 2 >= beta)) {
        return beta;
    }

    //-- Next Principle Variation
    struct t_pv_data *next_pv = &(board->pv_data[ply + 1]);

    //-- Define the local variables
    pv->legal_moves_played = 0;
    t_chess_value best_score;
    t_chess_value a = alpha;
    t_chess_value b = beta;
	t_chess_value e;

    // Declare local variables
    t_undo undo[1];

    //-- Generate All Moves
    struct t_move_list moves[1];
    moves->hash_move = NULL;

    //-----------------------------------------------
    //-- Handling In-Check (e.g. Cannot Stand-Pat)
    //-----------------------------------------------
    if (board->in_check) {

        best_score = -CHECKMATE;

        //-- Generate moves which get out of check
        generate_evade_check(board, moves);

        // Are we in checkmate?
        if (moves->count == 0) {
            pv->best_line_length = ply;
            return -CHECKMATE + ply;
        }

        t_chess_value(*q_search)(struct t_board *board, int ply, int depth, t_chess_value alpha, t_chess_value beta);

        if (moves->count == 1)
            q_search = &qsearch_plus;
        else {
            q_search = &qsearch;
            //-- Order the moves
            order_evade_check(board, moves, ply);
        }

        //-- Play moves
        while (make_next_best_move(board, moves, undo)) {

            //-- Increment the "legal_moves_played" counter
            pv->legal_moves_played++;
            pv->current_move = moves->current_move;

            //-- Evaluate the new board position
            evaluate(board, next_pv->eval);

            //-- More than one move out of check so just use "vanilla" qsearch
            e = -q_search(board, ply + 1, depth - 1, -b, -a);

            //-- Is a research required?
            if (alpha + 1 != beta && e > a && a + 1 == b)
                e = -q_search(board, ply + 1, depth - 1, -beta, -a);

            unmake_move(board, undo);

            //-- Is it good enough to cut-off?
            if (e >= beta) {
                update_check_killers(pv, 0);
                poke(board->hash, e, ply, depth, HASH_LOWER, pv->current_move);
                return e;
            }

            //-- Is it the best so far?
            if (e > best_score) {
                best_score = e;

                //-- Does it improve upon alpha (i.e. is it part of the PV)?
                if (e > a) {
                    a = e;

                    //-- Update the Principle Variation
                    update_best_line(board, ply);
                }
            }

            // Reset the zero width window
            b = a + 1;

        }
    }
    else {
        //--------------------------------------------------------
        //-- Normal moves handled differently (e.g. can stand-pat)
        //--------------------------------------------------------

        //-- Does stand-pat cause a cut-off?
        e = pv->eval->static_score;
        if (e >= beta)
            return e;

        //-- Does the current value raise alpha?
        best_score = e;
        if (e > alpha) {
            a = e;
            pv->best_line_length = ply;
        }

        //-- Generate all captures
        generate_captures(board, moves);

        //-- Order the moves
        order_captures(board, moves);

        //-- Play *ALL* captures
        while (make_next_best_move(board, moves, undo)) {

            //-- Increment the "legal_moves_played" counter
            pv->legal_moves_played++;
            pv->current_move = moves->current_move;

			//-- Evaluate the new board position
			evaluate(board, next_pv->eval);

			//-- Search the next ply at reduced depth
			e = -qsearch(board, ply + 1, depth - 1, -b, -a);

			//-- Is a research required?
			if (alpha + 1 != beta && e > a && a + 1 == b)
				e = -qsearch(board, ply + 1, depth - 1, -beta, -a);

            unmake_move(board, undo);

            //-- Is it good enough to cut-off?
            if (e >= beta) {
                return e;
            }

            //-- Is it the best so far?
            if (e > best_score) {
                best_score = e;

                //-- Does it improve upon alpha (i.e. is it part of the PV)?
                if (e > a) {
                    a = e;
                    update_best_line(board, ply);
                }
            }

            // Reset the zero width window
            b = a + 1;

        }

		//-- Reset the move count
		moves->count = 0;

        //-- Now Try the checks!
		generate_quiet_checks(board, moves);

		////-- Maybe if there are many, one will be a checkmate?
		//if (moves->count > 4){

		//	while (simple_make_next_move(board, moves, undo)) {

		//		assert(board->in_check);

		//		//-- Generate moves
		//		struct t_move_list evade_check_moves[1];
		//		generate_evade_check(board, evade_check_moves);

		//		//-- Take the move back
		//		unmake_move(board, undo);

		//		//-- Is it Checkmate?
		//		if (evade_check_moves->count == 0){
		//			pv->best_line_length = ply + 1;
		//			e = +CHECKMATE - ply - 1;
		//			return e;
		//		}
		//	}
		//	//-- Reset the real move count 
		//	moves->imove = moves->count;
		//}
		
        //-- Order the moves
        order_moves(board, moves, ply);

        //-- Play moves
        while (make_next_best_move(board, moves, undo)) {

            //-- Increment the "legal_moves_played" counter
            pv->legal_moves_played++;
            pv->current_move = moves->current_move;

            //-- Evaluate the new board position
            evaluate(board, next_pv->eval);

            //-- Search the next ply at reduced depth
            e = -qsearch_plus(board, ply + 1, depth - 1, -b, -a);

            //-- Is a research required?
            if (alpha + 1 != beta && e > a && a + 1 == b)
                e = -qsearch_plus(board, ply + 1, depth - 1, -beta, -a);

            unmake_move(board, undo);

            //-- Is it good enough to cut-off?
            if (e >= beta) {
                poke(board->hash, e, ply, depth, HASH_LOWER, pv->current_move);
                update_killers(pv, 0);
                return e;
            }

            //-- Is it the best so far?
            if (e > best_score) {
                best_score = e;

                //-- Does it improve upon alpha (i.e. is it part of the PV)?
                if (e > a) {
                    a = e;
                    update_best_line(board, ply);
                }
            }

            // Reset the zero width window
            b = a + 1;

        }
    }

    //-- Update Hash
    if (best_score > alpha)
        poke(board->hash, best_score, ply, depth, HASH_EXACT, pv->best_line[ply]);

    // Return Best Score found
    return best_score;
}
Esempio n. 7
0
t_chess_value alphabeta(struct t_board *board, int ply, int depth, t_chess_value alpha, t_chess_value beta, BOOL early_cutoff, struct t_move_record *exclude_move) {

    //-- Should we call qsearch?
    if (depth <= 0 && !board->in_check)
        return qsearch_plus(board, ply, depth, alpha, beta);

    //-- Increment the nodes
    nodes++;

    //-- see if we need to update stats */
    if ((nodes & message_update_mask) == 0)
        uci_check_status(board, ply);

    //-- Local Principle Variation variable
    struct t_pv_data *pv = &(board->pv_data[ply]);

    //-- Has the maximum depth been reached
    if (ply >= MAXPLY || uci.stop) {
        pv->best_line_length = ply;
        assert(pv->eval->static_score >= -CHECKMATE && pv->eval->static_score <= CHECKMATE);
        return pv->eval->static_score;
    }

    /* check to see if this is a repeated position or draw by 50 moves */
    if (repetition_draw(board)) {
        pv->best_line_length = ply;
        return 0;
    }

    //-- Mate Distance Pruning
    if (CHECKMATE - ply <= alpha) {
        return alpha;
    }
    else if (-CHECKMATE + ply >= beta) {
        return beta;
    }
    else if ((!board->in_check) && (-CHECKMATE + ply + 2 >= beta)) {
        return beta;
    }

    //-- Declare local variables
    struct t_pv_data *next_pv = pv->next_pv;
    struct t_pv_data *previous_pv = pv->previous_pv;

    t_chess_value					best_score = -CHECKMATE;
    t_chess_value					a = alpha;
    t_chess_value					b = beta;

    //-- Determine what type of node it is
    if (beta > alpha + 1)
        pv->node_type = node_pv;
    else if (previous_pv->node_type == node_pv)
        pv->node_type = node_lite_all;
    else if (previous_pv->node_type == node_cut)
        pv->node_type = node_all;
    else
        pv->node_type = node_cut;

    //-- Probe Hash
    struct t_move_record *hash_move = NULL;
    struct t_hash_record *hash_record = probe(board->hash);

    //-- Has there been a match?
    if (hash_record != NULL) {

		//-- Get the score from the hash table
		t_chess_value hash_score = get_hash_score(hash_record, ply);
		
		//-- Could it make a cut-off?
		if (early_cutoff && hash_record->depth >= depth) {

            //-- Score in hash table is at least as good as beta
            if (hash_record->bound != HASH_UPPER && hash_score >= beta) {
                hash_record->age = hash_age;
                assert(hash_score >= -CHECKMATE && hash_score <= CHECKMATE);
                return hash_score;
            }

            //-- Score is worse than alpha
            if (hash_record->bound != HASH_LOWER && hash_score <= alpha) {
                hash_record->age = hash_age;
                assert(hash_score >= -CHECKMATE && hash_score <= CHECKMATE);
                return hash_score;
            }

            //-- Score is more accurate
            if (hash_record->bound == HASH_EXACT) {
                hash_record->age = hash_age;
                pv->best_line_length = ply;
                update_best_line_from_hash(board, ply);
                assert(hash_score >= -CHECKMATE && hash_score <= CHECKMATE);
                return hash_score;
            }
        }

        //-- Store the hash move for further use!
        hash_move = hash_record->move;

        //-- Use the hash score to refine the node type
        if (hash_record->bound != HASH_UPPER && hash_score >= beta)
            pv->node_type = node_super_cut;

        else if (hash_record->bound != HASH_LOWER && hash_score <= alpha)
            pv->node_type = node_super_all;

        else if (hash_record->bound == HASH_EXACT && pv->node_type == node_all)
            pv->node_type = node_lite_all;
    }

    //-- Beta pruning
    if (early_cutoff && depth <= 4 && pv->node_type != node_pv && beta < MAX_CHECKMATE && beta > -MAX_CHECKMATE && !board->in_check) {

        int pessimistic_score = pv->eval->static_score - depth * 50 - 100;

        if (pessimistic_score >= beta)
            return pessimistic_score;
    }

	//-- Razoring.
	t_chess_value e;	
	if (early_cutoff && (depth <= 4) && pv->node_type != node_pv  && !board->in_check){

		t_chess_value razor_margin = depth * 50 + 50;
		if (pv->eval->static_score + razor_margin <= alpha){

			t_chess_value razor_alpha = alpha - razor_margin;
			e = qsearch_plus(board, ply, depth, razor_alpha, razor_alpha + 1);
			
			if (e <= razor_alpha)
				return e;
		}
	}
		
    //-- Null Move
    t_undo undo[1];

	pv->mate_threat = 0;
	pv->null_refutation = NULL;
	pv->extension = FALSE;
	if (early_cutoff && can_do_null_move(board, pv, ply, alpha, beta)) {

		//-- Calculate Reduction
		//int r = (800 + 70 * depth) / 256 + min(3, (pv->eval->static_score - beta) / 128);
		int r = min(4, 2 + (25 * depth) / 128 + (pv->eval->static_score - beta) / 128);
		//int r = 3;

		//-- Make the changes on the board
		make_null_move(board, undo);

		//-- Store the move in the PV data
		pv->current_move = NULL;

		//-- Clear the Killer +2
		if (ply + 2 <= MAXPLY){
			board->pv_data[ply + 2].killer1 = NULL;
			board->pv_data[ply + 2].killer2 = NULL;
		}

		//-- Evaluate the new board position
		evaluate(board, next_pv->eval);

		//-- Find the new score
		e = -alphabeta(board, ply + 1, depth - r - 1 , -beta, -beta + 1, TRUE, NULL);

		//-- undo the null move
		unmake_null_move(board, undo);

		//-- is it good enough for a cut-off?
		if (e >= beta) {
			if (e > MAX_CHECKMATE)
				e = beta;
			poke(board->hash, e, ply, depth, HASH_LOWER, NULL);
			return e;
		}

		//-- Is there a Mate Threat after a super-reduced move - if so then exit?
		if (e < -MAX_CHECKMATE){
			if (pv->previous_pv->reduction > 1)
				return alpha;
			pv->mate_threat = e;
		}

		//-- Record the move which refuted the NULL move
		if (ply < MAXPLY)
			pv->null_refutation = board->pv_data[ply + 1].current_move;
    }

    //-- Internal Iterative Deepening!
	if (hash_move == NULL && !uci.stop){

		//-- PV Nodes - we *really* need a good move
		if (pv->node_type == node_pv && depth > 2) {

			//-- Search with reduced depth
			e = alphabeta(board, ply, depth - 2, alpha, beta, FALSE, NULL);

			//-- If still no move then search with -INFINITY bound
			if (e <= alpha)
				e = alphabeta(board, ply, depth - 2, -CHESS_INFINITY, beta, FALSE, NULL);

			//-- Probe the hash
			hash_record = probe(board->hash);

			//-- Set the hash move
			if (hash_record != NULL)
				hash_move = hash_record->move;
		}

		//-- Fail high nodes
		//else if ((pv->node_type == node_cut || pv->node_type == node_super_cut) && (depth >= 7) && alpha > -MAX_CHECKMATE && beta < MAX_CHECKMATE){

		//	//-- Search with reduced depth
		//	e = alphabeta(board, ply, depth / 2, alpha, beta);

		//	//-- If still no move then search with -INFINITY bound
		//	if (e <= alpha)
		//		e = alphabeta(board, ply, depth / 2, -CHESS_INFINITY, beta);

		//	//-- Probe the hash
		//	hash_record = probe(board->hash);

		//	//-- Set the hash move
		//	if (hash_record != NULL)
		//		hash_move = hash_record->move;

		//}
    }

    //-- Generate All Moves
    struct t_move_list moves[1];
    moves->hash_move = hash_move;

    if (board->in_check) {
        generate_evade_check(board, moves);

        // Are we in checkmate?
        if (moves->count == 0) {
            pv->best_line_length = ply;
			e = -CHECKMATE + ply;
            return e;
        }
        order_evade_check(board, moves, ply);
    }
    else {
        generate_moves(board, moves);
        order_moves(board, moves, ply);
    }

    //-- Enhanced Transposition Cutoff?
	t_chess_color to_move = board->to_move;
	if (early_cutoff && (depth > 4) && pv->node_type != node_pv && beta < MAX_CHECKMATE && alpha > -MAX_CHECKMATE && !uci.stop) {
        BOOL fail_low;
        while (simple_make_next_move(board, moves, undo)) {

            //-- Calculate Reduction Conservatively i.e. assume minimum reduction
			if (board->in_check)
				pv->reduction = 0;
			else if (PIECETYPE(moves->current_move->piece) == PAWN && COLOR_RANK(to_move, moves->current_move->to_square) >= 6)
				pv->reduction = 0;
			else
				pv->reduction = 1;

            //-- Simple version of alpha_beta for tips of search
            e = -alphabeta_tip(board, ply + 1, depth - pv->reduction, -beta, &fail_low);

            //-- Take the move back
            unmake_move(board, undo);

            //-- Is it good enough for a cutoff?
            if (e >= beta) {
                poke(board->hash, e, ply, depth, HASH_LOWER, moves->current_move);
                assert(e >= -CHECKMATE && e <= CHECKMATE);
                return e;
            }

            //-- Is it going to enhance the move ordering?
            if (fail_low) {
                moves->value[moves->imove] += MOVE_ORDER_ETC;
                assert(moves->move[moves->imove] == moves->current_move);
            }

        }
        //-- Reset the real move count for the "proper" search
        moves->imove = moves->count;
    }

	////-- No Hash Move for Cut Nodes with score below beta and no good capture and no ETC
	//if (hash_move == NULL && (any_fail_low == FALSE) && ((pv->node_type == node_cut) || (pv->node_type == node_super_all)) && (depth >= 3) && (beta > pv->eval->static_score) && no_good_captures(board, moves)){

	//	//-- Search with reduced depth
	//	e = alphabeta(board, ply, depth - 3, alpha, beta);

	//	//-- If still no move then search with -INFINITY bound
	//	if (e <= alpha)
	//		e = alphabeta(board, ply, depth - 3, -CHESS_INFINITY, beta);

	//	//-- Probe the hash
	//	hash_record = probe(board->hash);

	//	//-- Set the hash move
	//	if (hash_record != NULL)
	//		hash_move = hash_record->move;
	//}

    //-- Create the list of "bad" captures
    struct t_move_list bad_moves[1];
    bad_moves->count = 0;
    bad_moves->imove = 0;

    //-- Reset the move count (must be after IID)
    pv->legal_moves_played = 0;

    //-- Variables used to calculate the reduction
	t_chess_color opponent = OPPONENT(to_move);
    BOOL in_check = board->in_check;
    struct t_move_record *last_move = NULL;
	if (ply > 1)
        last_move = board->pv_data[ply - 2].current_move;


    //-- Play moves
    while (!uci.stop && make_next_move(board, moves, bad_moves, undo)) {

        //-- Increment the "legal_moves_played" counter
        pv->legal_moves_played++;
        pv->current_move = moves->current_move;

		//========================================//
		// Futility Pruning
		//========================================//
		if (uci.options.futility_pruning && is_futile(pv, next_pv, depth, a, b)){
			unmake_move(board, undo);
			continue;
		}		
		
		//-- Clear the Killer +2
		if (ply + 2 <= MAXPLY){
			board->pv_data[ply + 2].killer1 = NULL;
			board->pv_data[ply + 2].killer2 = NULL;
		}

		//-- Evaluate the new board position
        evaluate(board, next_pv->eval);

		////========================================//
		//// See if Extension is Necessary
		////========================================//
		//pv->extension = FALSE;
		//if (pv->mate_threat){

		//	e = -alphabeta(board, ply + 1, depth - 1, -CHECKMATE, pv->mate_threat + 2);

		//	pv->extension = (e > pv->mate_threat);

		//	if (e <= pv->mate_threat && e <= a)
		//	{
		//		unmake_move(board, undo);
		//		continue;
		//	}
		//}
			
		//========================================//
		// Calculate reduction
		//========================================//

		struct t_move_record *current_move = pv->current_move;

		//-- In Check?
		if (board->in_check){
			if (see_safe(board, current_move->to_square, 0))
				pv->reduction = 0;
			else if (ply > 3 && board->pv_data[ply - 1].in_check && board->pv_data[ply - 3].in_check)
				pv->reduction = 0;
			else
				pv->reduction = 1;
		}

		//-- Pawn push to 7th
		else if (PIECETYPE(current_move->piece) == PAWN && COLOR_RANK(to_move, current_move->to_square) >= 6){
			
			//-- Pawn Promotion
			if (current_move->promote_to){
				
				if (pv->legal_moves_played == 1 || PIECETYPE(current_move->promote_to) == QUEEN){

					//--Extend if it's a safe pawn promotion or first move
					if ((pv->current_move->captured && moves->current_move_see_positive) || see_safe(board, current_move->to_square, 0))
						pv->reduction = 0;
					else
						pv->reduction = 1;
				}

				// Reduce Heavily if not a queen promotion
				else
					pv->reduction = 5;
			}

			//-- Push to the 7th
			else if (pv->legal_moves_played == 1 || (pv->current_move->captured && moves->current_move_see_positive) || see_safe(board, current_move->to_square, 0))
				pv->reduction = 0;
			else
				pv->reduction = 1;

		}

		//-- First Move?
		else if (pv->legal_moves_played == 1)
			pv->reduction = 1;

		////-- Under Threat of Mate
		//else if (pv->mate_threat)
		//	pv->reduction = 1;

		//-- Good Capture?
		else if (pv->current_move->captured && moves->current_move_see_positive){
			pv->reduction = 1;
		}

		//-- Is this getting out of check?
		else if (in_check){
			if (pv->current_move == pv->check_killer1)
				pv->reduction = 1;
			else if (PIECETYPE(current_move->piece) == KING){
				if (CAN_CASTLE(to_move, board->castling))
					pv->reduction = 4;
				else
					pv->reduction = 1;
			}
			else if (current_move->captured) /* must be a bad capture */
				pv->reduction = 2;
			else if (see_safe(board, current_move->to_square, 0))
				pv->reduction = 1;
			else
				pv->reduction = 2;
		}

		//-- Don't reduce Killers!
		else if (pv->current_move == pv->killer1){
			pv->reduction = 1;
		}

		//-- Does it move a threatened piece?
		else if (pv->null_refutation != NULL && pv->null_refutation->to_square == pv->current_move->from_square){

			if (see_safe(board, current_move->to_square, 0))
				pv->reduction = 1;
			else
				pv->reduction = 3;
		}

		//-- Candidate for serious reductions
		else{

			switch (pv->node_type)
			{
			case node_cut:
				pv->reduction = 3;
				if (pv->current_move->captured)
					pv->reduction += 1; 
				break;

			case node_super_cut:
				pv->reduction = 4;
				if (pv->current_move->captured)
					pv->reduction += 1; 
				break;

			case node_pv:
				if (pv->legal_moves_played > 2)
					pv->reduction = 2;
				else
					pv->reduction = 1;
				break;

			case node_lite_all:
				if (pv->legal_moves_played > 2)
					pv->reduction = 2;
				else
					pv->reduction = 1;
				
				if (pv->current_move->captured)
					pv->reduction += 1;				
				break;

			case node_super_all:
				if (current_move->captured){
					if (pv->legal_moves_played < 4)
						pv->reduction = 3;
					else
						pv->reduction = 4;
				}
				else if (!see_safe(board, current_move->to_square, 0)){
					if (pv->legal_moves_played < 4)
						pv->reduction = 4;
					else if (pv->legal_moves_played < 12)
						pv->reduction = 5;
					else 
						pv->reduction = 6;
				}
				else if (pv->legal_moves_played < 4)
					pv->reduction = 2;
				else if (pv->legal_moves_played < 12)
					pv->reduction = 3;
				else
					pv->reduction = 4;
				break;

			case node_all:
				if (current_move->captured){
					if (pv->legal_moves_played < 4)
						pv->reduction = 3;
					else 
						pv->reduction = 4;
				}
				else if (!see_safe(board, current_move->to_square, 0)){
					if (pv->legal_moves_played < 4)
						pv->reduction = 4;
					else 
						pv->reduction = 5;
				}
				else if (pv->legal_moves_played < 4)
					pv->reduction = 2;
				else if (pv->legal_moves_played < 18)
					pv->reduction = 3;
				else
					pv->reduction = 4;
				break;

			}

		}

        //-- Search the next ply at reduced depth
        e = -alphabeta(board, ply + 1, depth - pv->reduction, -b, -a, TRUE, NULL);

        //-- Fail high on a super-reduced move?
        if (e > a && pv->reduction > 1) {
            pv->reduction = 1;

            //-- Search again using the full width
            e = -alphabeta(board, ply + 1, depth - 1, -beta, -a, TRUE, NULL);
        }

        //-- Is a research required?
        else if (alpha + 1 != beta && e > a && a + 1 == b)
            e = -alphabeta(board, ply + 1, depth - pv->reduction, -beta, -a, TRUE, NULL);

        unmake_move(board, undo);

        //-- Is it good enough to cut-off?
        if (e >= beta) {
            if (board->in_check)
                update_check_killers(pv, depth);
            else
                update_killers(pv, depth);

            //-- Record the cutoff
            cutoffs++;
            if (pv->legal_moves_played == 1)
                first_move_cutoffs++;

            //-- Store in the hash table
            poke(board->hash, e, ply, depth, HASH_LOWER, pv->current_move);
            return e;
        }

        //-- Is it the best so far?
        if (e > best_score) {
            best_score = e;

            //-- Does it improve upon alpha (i.e. is it part of the PV)?
            if (e > a) {
                a = e;

                //-- Update the Principle Variation
                update_best_line(board, ply);
            }
        }

        // Reset the zero width window
        b = a + 1;

		//-- Was this a fail low at a node which should have failed high?
		//if (pv->node_type == )
    }

    //-- Is it a draw
    if (pv->legal_moves_played == 0) {
        pv->best_line_length = ply;
        return 0;
    }

    //-- Update Hash
    if (best_score > alpha)
        poke(board->hash, best_score, ply, depth, HASH_EXACT, pv->best_line[ply]);
	else
        poke(board->hash, best_score, ply, depth, HASH_UPPER, NULL);

    // Return Best Score found
    assert(best_score >= -CHECKMATE && best_score <= CHECKMATE);
    return best_score;

}