Exemplo n.º 1
0
BOOL test_alt_move_gen() {

    BOOL ok = TRUE;
    struct t_move_list moves[1], xmoves[2];

    set_fen(position, "5rN1/4P3/1B6/1B3k2/8/4P3/6PP/2RQK2R w K -");
    generate_captures(position, moves);
    generate_quiet_checks(position, moves);
    generate_no_capture_no_checks(position, moves);

    generate_moves(position, xmoves);

    ok &= (equal_move_lists(xmoves, moves));

    flip_board(position);
    generate_captures(position, moves);
    generate_quiet_checks(position, moves);
    generate_no_capture_no_checks(position, moves);

    generate_moves(position, xmoves);
    ok &= (equal_move_lists(xmoves, moves));

    return ok;

}
Exemplo n.º 2
0
/* 評価関数 */
int eval_func(void)
{
    int value;
    XY moves[MOVENUM];
    int x, y;
    
    // 合法手数(自由度)の差を評価関数とする
    value = generate_moves(turn, moves);
    value -= generate_moves(-turn, moves);
    
    // 自由度1につき30としておく
    value *= 30;
    
    for (x = 0; x < 8; x++)
    {
        for (y = 0; y < 8; y++)
        {
            if (board[x][y] == turn)
                value += eval_board[x][y];
            else if (board[x][y] == -turn)
                value -= eval_board[x][y];
        }
    }
    
    return value;
}
Exemplo n.º 3
0
void perft_divide(FILE* stream, Board* board, unsigned int depth)
{
	int i, totalMoves = 0;
	Move moves[256];

	if (depth <= 0)
	{
		return;
	}

	int num_moves = generate_moves(board, moves);

	for (i = 0; i < num_moves; ++i)
	{
		int numDividedMoves = 0;
		char moveString[8];
		sprint_move(moveString, moves[i]);
		printf("%s ", moveString);

		make_move(board, moves[i]);
		numDividedMoves = perft_perft(board, depth - 1);
		unmake_move(board, moves[i]);

		totalMoves += numDividedMoves;
		printf("%i\n", numDividedMoves);
	}

	fprintf(stream, "\nMoves: %i\n", num_moves);
	fprintf(stream, "Nodes: %i\n", totalMoves);
}
Exemplo n.º 4
0
int quiescent(app_t *app, cnodeptr_t parent, int alpha, int beta)
{
	int i, score = -MATE;
	node_t node;
	int delta = 200;

	init_node(&node);

	assert(app);

	app->search.nodes++;

	/* max depth */
	if (app->game.board->ply > (SEARCH_MAXDEPTH - 1)) {
		return evaluate(app->game.board);
	}

	/* draws */
	if (repetitions(app) || (app->game.board->half >= 100)) {
		return 0;
	}

	score = evaluate(app->game.board);

	if (score >= beta) return beta;
	/* delta pruning based on a material value of delta (this should be disabled
	   in the endgame) */
	/* The idea here is that, even if our score can improve alpha, it doesn't
	   improve it by a significant amount, so don't bother searching these nodes */
	if (score < (alpha - delta)) return alpha;
	if (score > alpha) alpha = score;


	/* generate moves (with separate captures) */
	generate_moves(app->game.board, &node.ml, &node.cl);

	for (i = 0; i < node.cl.count; i++) {
		/* get the next move ordered */
		next_move(i, &node.cl);

		if (!(do_move(app->game.board, &app->game.undo, node.cl.moves[i])))
			continue;

		score = -quiescent(app, &node, -beta, -alpha);

		node.made++;
		undo_move(app->game.board, &app->game.undo);

		if (score > alpha) {
			if (score >= beta) {
				return beta;
			}

			/* update alpha */
			alpha = score;
		}
	}

	return alpha;
}
Exemplo n.º 5
0
int PVS(int alpha, int beta, int depth)
{
    if(is_draw_by_repetition_or_50_moves()) return DRAW;
    
    if(depth == 0) return quiescence(alpha, beta);
    
    Move movelist[256];
    int n = generate_moves(movelist);
    if(n == 0)
    {
        if(!in_check(turn_to_move))
            return DRAW;
        return LOSING + ply - begin_ply;
    }
    sorting_moves(movelist, n);
    
    int bool_search_pv = 1;
    Move bestmove = 0;

    for(int i = 0; i < n; i += 1)
    {
        Move i_move = movelist[i];
        make_move(i_move);
        int score;
        if(bool_search_pv)
        {
            score = -PVS(-beta, -alpha, depth - 1);
        }
        else
        {
            score = -ZWS(-alpha, depth - 1, 1);
            if(score > alpha)
                score = -PVS(-beta, -alpha, depth - 1);
        }
        unmake_move(i_move);
        
        if(score >= beta)
        {
            hash_save_entry(depth, beta, i_move, MORE_THAN_BETA);
            if(!move_broken(i_move))
            {
                history[board[move_from(i_move)]][move_to(i_move)] = depth * depth;
            }
            return beta;
        }
        if(score > alpha)
        {
            bestmove = i_move;
            alpha = score;
        }
        bool_search_pv = 0;
    }
    if(bestmove != 0)
        hash_save_entry(depth, alpha, bestmove, BETWEEN_ALPHA_AND_BETA);
    else
        hash_save_entry(depth, alpha, 0, LESS_THAN_ALPHA);
    return alpha;
}
Exemplo n.º 6
0
// min
int min_node(int depth, int side, XY *move)
{
    int best = INFINITY, value;
    int nmoves;
    XY moves[MOVENUM], opp[MOVENUM];
    int pre_board[8][8];
    int i;
    
    if (depth == DEPTH)
        return eval_func();
    
    // 手を生成
	nmoves = generate_moves(side, moves);
    
    // 手がないとき
	if (nmoves == 0)
	{
		if (generate_moves(-side, opp) == 0) // 相手もおけないときは終了
			return get_terminal_value();
		else    // 違うときはパス
			moves[nmoves++] = PASSMOVE;
	}
    
    // たどっていく
    for (i = 0; i < nmoves; i++)
	{
        memcpy(pre_board, board, sizeof(board));    // 盤面の保存
		place_disk(side, moves[i]);   // 一手進める
        
        // 再帰(recursive)
		value = max_node(depth + 1, -side, move);
        
        memcpy(board, pre_board, sizeof(board));    // 戻す
        
        // 値の更新
        if (value <= best)
        {
            best = value;
            if (depth == 0)
                *move = moves[i];
        }
	}
    
	return best;
}
Exemplo n.º 7
0
Arquivo: move.c Projeto: jes/zoe
/* return 1 if the move is valid and 0 otherwise, printing an appropriate
 * message if print is non-zero.
 */
int is_valid_move(Game game, Move m, int print) {
    Board *board = &(game.board);
    Game game2 = game;
    uint64_t beginbit, endbit;
    char *strmove = xboard_move(m);

    beginbit = 1ull << m.begin;
    endbit = 1ull << m.end;

    /* ensure that the piece belongs to the current player */
    if(!(board->b[game.turn][OCCUPIED] & beginbit)) {
        if(print)
            printf("Illegal move (%s): that is not your piece.\n", strmove);
        return 0;
    }

    /* ensure that the end tile can be moved to from the begin tile */
    if(!(generate_moves(&game, m.begin) & endbit)) {
        if(print)
            printf("Illegal move (%s): that piece can't move like that.\n",
                    strmove);
        return 0;
    }

    /* ensure that pawns reaching the eighth rank promote */
    if(!m.promote && ((m.end / 8) == 0 || (m.end / 8) == 7)
            && board->mailbox[m.begin] == PAWN) {
        if(print)
            printf("Illegal move (%s): pawns reaching the eighth rank must "
                    "promote.\n", strmove);
        return 0;
    }

    /* ensure that no other pieces promote */
    if(m.promote && (board->mailbox[m.begin] != PAWN
                || ((m.end / 8) != 0 && (m.end / 8) != 7))) {
        if(print)
            printf("Illegal move (%s): that piece may not promote at this "
                    "time.\n", strmove);
        return 0;
    }

    /* ensure that the king is not left in check
     * NOTE: we apply_move() here, so the state of the game is changed; this
     * check must be done last
     */
    apply_move(&game2, m);
    if(king_in_check(&(game2.board), game.turn)) {
        if(print)
            printf("Illegal move (%s): king is left in check.\n", strmove);
        return 0;
    }

    /* hasn't failed any validation, must be a valid move */
    return 1;
}
Exemplo n.º 8
0
int ZWS(int beta, int depth, int can_reduce)
{
    if(is_draw_by_repetition_or_50_moves()) return DRAW;
    
    Entry *entry = hash_get_entry();
    if(entry != NULL && entry->depth >= depth)
    {
        int eval = entry->eval, flag = entry->flag;
        if(flag != LESS_THAN_ALPHA && eval >= beta)
            return beta;
        if(flag != MORE_THAN_BETA && eval < beta)
            return beta - 1;
    }
    
    if(depth == 0) return quiescence(beta - 1, beta);
    
    /*if(depth <= 6 && evaluate(beta - 1, beta) >= beta)
    {
        return beta;
    }*/
    
    if(depth > 2 && can_reduce && !in_check(turn_to_move))
    {
        int R = depth > 6 ? 3: 2;
        make_null_move();
        int score = -ZWS(-beta + 1, depth - R - 1, 0);
        unmake_null_move();
        if(score >= beta) return beta;
    }
    
    Move movelist[256];
    int n = generate_moves(movelist);
    if(n == 0)
    {
        if(!in_check(turn_to_move))
            return DRAW;
        return LOSING + ply - begin_ply;
    }
    sorting_moves(movelist, n);
    for(int i = 0; i < n; i += 1)
    {
        Move i_move = movelist[i];
        make_move(i_move);
        int score = -ZWS(-beta + 1, depth - 1, can_reduce);
        unmake_move(i_move);
        
        if(score >= beta)
        {
            hash_save_entry(depth, beta, i_move, MORE_THAN_BETA);
            return beta;
        }
    }
    hash_save_entry(depth, beta - 1, 0, LESS_THAN_ALPHA);
    return beta - 1;
}
Exemplo n.º 9
0
BOOL test_make_unmake() {

    BOOL ok = TRUE;
    int i, j;

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

    set_fen(position, "rnbqkb1r/ppppp1pp/7n/5p2/4P3/8/PPPP1PPP/RNBQKBNR w KQkq -");
    assert(integrity(position));
    for (j = WHITE; j <= BLACK; j++) {
        generate_moves(position, moves);
        for (i = 0; i < moves->count; i++) {
            assert(integrity(position));
            if (make_move(position, moves->pinned_pieces, moves->move[i], undo)) {
                assert(integrity(position));
                unmake_move(position, undo);
                assert(integrity(position));
            }
        }
        flip_board(position);
    }
    ok = integrity(position);

    set_fen(position, "r3Rbk1/2p2p1p/p2p4/1p1P2q1/8/PBPQ2pP/1P3P1P/3R2K1 b - -");
    assert(integrity(position));
    for (j = WHITE; j <= BLACK; j++) {
        generate_moves(position, moves);
        for (i = 0; i < moves->count; i++) {
            assert(integrity(position));
            if (make_move(position, moves->pinned_pieces, moves->move[i], undo)) {
                assert(integrity(position));
                unmake_move(position, undo);
                assert(integrity(position));
            }
        }
        flip_board(position);
    }
    ok &= integrity(position);

    return ok;
}
bool move_is_legal(const Position& pos, const Move m) {

  MoveStack mlist[256];
  MoveStack *cur, *last = generate_moves(pos, mlist, true);

   for (cur = mlist; cur != last; cur++)
      if (cur->move == m)
          return pos.pl_move_is_legal(m, pos.pinned_pieces(pos.side_to_move()));

  return false;
}
Exemplo n.º 11
0
static int find_move(app_t *app, move_t m)
{
	int i;
	movelist_t ml;
	generate_moves(app->game.board, &ml, &ml);

	for (i = 0; i < ml.count; i++) {
		if (ml.moves[i] == m) return TRUE;
	}

	return FALSE;
}
Exemplo n.º 12
0
void init_new_child(struct position pos, struct position *new_pos, int turn, int i)  
{
	int j;

	new_pos->nc = 0;

	for (j = 0; j < 32; j++) {
		new_pos->board[j] = pos.child_ptr[(i * 32) + j];
	}

	generate_moves(new_pos->board, (turn + 1) % 2, &(new_pos->nc), new_pos->child_ptr);
}
Exemplo n.º 13
0
int main(int argc, char **argv)
{
    const int human_side = (argc >= 2) ? atoi(argv[1]) : 0; // 先攻と後攻の選択
    XY moves[MOVENUM];
    
    init_board();   // 盤面の初期化
    print_board();  // 盤面の表示
    
    srand((unsigned)time(NULL)); // 乱数列の初期化
    
    int turn;
    
    for (turn = 1;; turn *= -1)   // ターンを交代
    {
        XY nextmove;
        
        // どちらも置けなくなったときは終了
        if (generate_moves(turn, moves) == 0
            && generate_moves(-turn, moves) == 0)
            break;
        
        
        if (turn == human_side)
            man_player(turn, &nextmove);
        else
            com_player(turn, &nextmove);
        
        // パスかどうかの判定
        if (nextmove.x != PASSMOVE.x && nextmove.y != PASSMOVE.y)
        {
            printf("%s -> %c%c\n\n",
                   (turn == BLACK ? "BLACK" : "WHITE"),
                   'a' + nextmove.x, '1' + nextmove.y);
            place_disk(turn, nextmove); // ディスクの設置
            print_board();          // 盤面の表示
        }
    }
    show_result();  // 結果の表示
    return 0;
}
Exemplo n.º 14
0
Arquivo: move.c Projeto: jes/zoe
/* return a list of moves that can be played from the given position */
void generate_movelist(Game *game, Move *movelist, int *nmoves) {
    uint64_t pieces = game->board.b[game->turn][OCCUPIED];
    Move m;
    int nmove = 0;

    /* for each of the pieces... */
    while(pieces) {
        /* pick the next piece */
        int piece = bsf(pieces);

        /* remove this piece from the set */
        pieces ^= 1ull << piece;

        /* set the start square of the moves */
        m.begin = piece;

        /* generate the moves for this piece */
        uint64_t moves = generate_moves(game, piece);

        /* for each of this piece's moves */
        while(moves) {
            /* pick the next move */
            int move = bsf(moves);

            /* remove this move from the set */
            moves ^= 1ull << move;

            /* set the end square of this move */
            m.end = move;

            /* promote pawns */
            if(game->board.mailbox[piece] == PAWN
                    && (m.end / 8 == 0 || m.end / 8 == 7)) {
                m.promote = QUEEN;
                movelist[nmove++] = m;
                m.promote = KNIGHT;
                movelist[nmove++] = m;
                m.promote = ROOK;
                movelist[nmove++] = m;
                m.promote = BISHOP;
                movelist[nmove++] = m;
            } else {
                m.promote = 0;
                movelist[nmove++] = m;
            }
        }
    }

    *nmoves = nmove;
}
Exemplo n.º 15
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;

}
Exemplo n.º 16
0
void test_generate_moves_default()
{
    // generate_moves
    CHESS_STATE_STR css = {0};
    strcpy(css.piece_placement, "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR");
    CHESS_STATE cs = get_chess_state(css);
    generate_moves(&cs);

    // check addnextstate has set default values correctly
    CHESS_STATE ns = cs.child_head[0];
    assert(ns.full_move_number==cs.full_move_number+1);
    assert(ns.half_move_clock==cs.half_move_clock+1);
    assert(ns.flg_is_white_move!=cs.flg_is_white_move);
    assert(ns.parent==&cs);
}
Exemplo n.º 17
0
/* ランダムに配置 */
void generate_random_move(const int side, XY *move)
{
    XY legal_moves[60];
    int x, y;
    int nmoves;

    nmoves = generate_moves(side, legal_moves); // 合法手を作る
    if (nmoves == 0)    // パス
	{
        printf("Pass!\n");
        *move = PASSMOVE;
        return ;
	}
    
    *move = legal_moves[rand() % nmoves];   // 移動する場所を代入
    return ;
}
Exemplo n.º 18
0
/* -----------------------------------------------------
	This gets all the child moves
	and compares them to a string of moves

	ALL the moves must match
	----------------------------------------------------*/
void check_contains_moves(CHESS_STATE *current_state, char *expected_moves)
{
    CHESS_STATE *child_states;

    char buffer[100] = {0};

    strcpy(buffer,expected_moves);
    generate_moves(current_state);

    char *next_expected_move = strtok(buffer, ",");

    while(next_expected_move != NULL)
    {
        char *move_text;
        int found = 0;

        for (child_states = current_state->child_head;
                child_states != NULL;
                child_states = child_states->next)
        {
            move_text = get_move(child_states,0);

            if (strcmp(move_text, next_expected_move)==0)
            {
                found=1;
                break;
            }
        }
        if (found==0)
        {
            printf("%s not found!\n",next_expected_move);
            for (child_states = current_state->child_head;
                    child_states!=NULL;
                    child_states = child_states->next)
            {
                move_text=get_move(child_states,0);
                printf("%s ",move_text);
            }
            exit(1);
        }

        next_expected_move = strtok(NULL,",");
    }
    delete_nodes(current_state);
}
Exemplo n.º 19
0
U64 perft(int depth)
{
    Move movelist[256];
    int n;
    U64 result = 0;
    
    if(depth == 0) return 1;
    
    n = generate_moves(movelist);
    for(int i = 0; i < n; i += 1)
    {
        Move i_move = movelist[i];
        make_move(i_move);
        result += perft(depth - 1);
        unmake_move(i_move);
    }
    return result;
}
Exemplo n.º 20
0
/* ランダムに手を生成する関数 */
void randam_player(const int side, XY *move)
{
    XY moves[MOVENUM];
    int nmoves;
    
	printf( "Com Thinking...\n");
    
    nmoves = generate_moves(side, moves);
    
    // 手がなければパス
    if (nmoves == 0)
    {
        printf("Pass!\n\n");
        *move = PASSMOVE;
        return ;
    }
    *move = moves[rand() % nmoves];
}
Exemplo n.º 21
0
/* COMの手を生成する関数 */
void com_player(const int side, XY *move)
{
    XY moves[MOVENUM];
    int value;
	printf( "Com Thinking...\n");
    
    // 手がなければパス
    if (generate_moves(side, moves) == 0)
    {
        printf("Pass!\n\n");
        *move = PASSMOVE;
        return ;
    }
    
    value = max_node(0, side, move); // Min-Max法で手を生成
    printf("value = %d\n", value);
    
    if (value == INFINITY)  // 勝ち
        printf("COM Finds Win!\n");
}
Exemplo n.º 22
0
// Reports game status for current position or after the given move. The status
// helps to determine whether to continue with search or if the game is over.
//------------------------------------------------------------------------------
Status standing(Position *self, Move move, int score) {
    Status status = InProgress;

    if (move) {
        self = make_move(self, move);
    }

    int ply = PLY(); // After the ^^^ move is made.

    if (score == 0) {
        if (ply == 1) {
            if (insufficient(self)) {
                status = Insufficient;
            } else if (third_repetition(self)) {
                status = Repetition;
            } else if (fifty(self)) {
                status = FiftyMoves;
            }
        }
        if (status == InProgress) {
            MoveGen *gen = new_gen(self, MaxPly);
            if (!any_valid(generate_moves(gen))) {
                status = Stalemate;
            }
        }
    } else if (score == Checkmate - ply) {
        if (is_in_check(self, self->color)) {
            status = (self->color == White ? BlackWon : WhiteWon);
        } else {
            status = Stalemate;
        }
    } else if (score > Checkmate - MaxDepth && (score + ply) / 2 > 0) {
        status = (self->color == White ? BlackWinning : WhiteWinning);
    }

    if (move) {
        self = undo_last_move(self);
    }

    return status;
}
Exemplo n.º 23
0
BOOL test_ep_capture()
{
	uci_set_mode();
	uci_isready();

	set_hash(512);
	set_own_book(TRUE);

	uci_position(position, "position fen 2r2bk1/1pNb4/3p3q/p2Pp2n/2P1PpPP/5P2/PPn1Q3/2KR3R b - g3");

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

	generate_moves(position, moves);
	for (int i = 0; i < moves->count; i++){
		if (make_move(position, moves->pinned_pieces, moves->move[i], undo))
			unmake_move(position, undo);
	}

	return TRUE;
}
Exemplo n.º 24
0
unsigned int perft_perft(Board* board, unsigned int depth)
{
	int i, nperft = 0;
	Move moves[256];

	if (depth == 0)
	{
		return 1;
	}

	TTEntry* ttentry = get_tt_entry_at_depth(board->zobrist, depth);
	if (ttentry)
	{
		return ttentry->score;
	}

	unsigned int numMoves = generate_moves(board, moves);

	for (i = 0; i < numMoves; ++i)
	{
		make_move(board, moves[i]);
		if ((board->sideToMove == BLACK &&
			!black_attacks_square(board, bit_scan_forward(board->pieces[WHITE_KING]))) ||
			(board->sideToMove == WHITE &&
			!white_attacks_square(board, bit_scan_forward(board->pieces[BLACK_KING]))))
		{
			nperft += perft_perft(board, depth - 1);
		}
		unmake_move(board, moves[i]);
	}

	TTEntry new_entry;
	new_entry.key = board->zobrist;
	new_entry.score = nperft;
	new_entry.depth = depth;

	put_tt_entry(&new_entry);

	return nperft;
}
Exemplo n.º 25
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;
}
Exemplo n.º 26
0
/* 人間の入力を管理する関数 */
void man_player(const int side, XY *move)
{
    XY moves[MOVENUM];
    char buf[1000];
    
    // 手がなければパス
    if (generate_moves(side, moves) == 0)
    {
        printf("Pass!\n");
		printf("Press Enter!\n");
		fgets(buf, sizeof(buf), stdin);
        *move = PASSMOVE;
        return ;
    }
    
    while (1)
    {
        // 手の入力
        do
		{
            printf("Where? ");
            fgets(buf, sizeof(buf), stdin);
		} while(strlen(buf) < 1
                || buf[0] < 'a' || buf[0] > 'h'
                || buf[1] < '1' || buf[1] > '8');
        
        move->x = buf[0] - 'a';
        move->y = buf[1] - '1';
        
        
        // 合法手かどうかの判定
        if (is_legal_move(side, *move))
            break;
        else
            printf("Illeagal Move\n\n");
    }
}
Exemplo n.º 27
0
void console()
{
    char str[100];
    Move movelist[256];
    int n;
    int depth_limit = 8;
    setup_position(start_fen);
    
    printf("white or black?\n");
    while(1)
    {
        gets(str);
        if(!strcmp(str, "white") || ! strcmp(str, "black"))
        {
            break;
        }
    }
    int is_reversed;
    if(!strcmp(str, "black"))
    {
        is_reversed = 1;
        print_position(is_reversed);
        Move bestmove = search(depth_limit);
        make_move(bestmove);
    }
    else
    {
        is_reversed = 0;
    }
    
    while(1)
    {
        print_position(is_reversed);
        n = generate_moves(movelist);
        if(n == 0)
        {
            while(1);
        }
        int flag = 1;
        while(flag)
        {
            gets(str);
            Move move = str_to_move(str);
            for(int i = 0; i < n; i += 1)
            {
                Move i_move = movelist[i]; 
                if(move == i_move)
                {
                    make_move(move);
                    flag = 0;
                    break;
                }
            }
            if(flag)
            {
                printf("Incorrect\n");
            }
        }
        
        print_position(is_reversed);
        Move bestmove = search(depth_limit);
        if(bestmove == 0)
        {
            while(1);
        }
        make_move(bestmove);
    }
}
Exemplo n.º 28
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;

}
Exemplo n.º 29
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;
}
Exemplo n.º 30
0
int alpha_beta(app_t *app, cnodeptr_t parent, int alpha, int beta, int depth)
{
	int palpha = alpha;
	int i, score = -MATE, highest = -MATE;
	node_t node;
	move_t cutoff = 0;
	piece_t p;

	init_node(&node);

	assert(app);

	app->search.nodes++;
	node.depth = depth;

	/* max depth */
	if (app->game.board->ply > (SEARCH_MAXDEPTH - 1)) {
		/* return evaluate(app->board); */
		return quiescent(app, parent, alpha, beta);
	}

	/* recursive base */
	if (depth == 0) {
		return evaluate(app->game.board);
	}

	/* draws */
	if (repetitions(app) || (app->game.board->half >= 100)) {
		return 0;
	}

	/* if we are checked, set the nodes checked flag */
	if (check(app->game.board, app->game.board->side)) {
		node.flags |= NODE_CHECK;
		/* extend our search by 1 depth if we are in check */
		/* NOTES: we may want to NOT extend our search here if the parent
		   is in check, because the means we already extended once */
		depth++;
	}

	/* TODO:
	     - NULL moves
	     - Late-move reduction
	     - Tactical extensions (pins & forks -> depth++)
	 */

	/* probe our table */
	if (probe_hash(&app->hash, app->game.board, &cutoff, &score, depth, alpha, beta) == TRUE) {
		app->hash.cut++;
		return score;
	}

	/* generate moves */
	generate_moves(app->game.board, &node.ml, &node.ml);

	/* reset score */
	score = -MATE;

	/* try to match our hash hit move */
	if (cutoff != 0) {
		for (i = 0; i < node.ml.count; i++) {
			if (node.ml.moves[i] == cutoff) {
				node.ml.scores[i] = 20000;
				break;
			}
		}
	}

	/* search negamax */
	for (i = 0; i < node.ml.count; i++) {
		/* get the next move ordered */
		next_move(i, &node.ml);

		if (!(do_move(app->game.board, &app->game.undo, node.ml.moves[i])))
			continue;

		score = -alpha_beta(app, &node, -beta, -alpha, depth - 1);

		node.made++;
		undo_move(app->game.board, &app->game.undo);

		/* score whatever is best so far */
		if (score > highest) {
			node.best = node.ml.moves[i];
			highest = score;

			/* update alpha */
			if (score > alpha) {
				if (score >= beta) {

					/* non-captures causing beta cutoffs (killers) */
					if (!is_capture(node.ml.moves[i])) {
						app->game.board->killers[1][app->game.board->ply] =
							app->game.board->killers[0][app->game.board->ply];
						app->game.board->killers[0][app->game.board->ply] = node.ml.moves[i];
					}

					/* store this beta in our transposition table */
					store_hash(&app->hash, app->game.board, node.best, beta, HASH_BETA, depth);

					return beta;
				}

				/* update alpha */
				alpha = score;

				/* update our history */
				if (!is_capture(node.best)) {
					p = app->game.board->pos.squares[move_from(node.best)];
					app->game.board->history[piece_color(p)][piece_type(p)][move_to(node.best)] += depth;
				}
			}
		}
	}

	/* check for checkmate or stalemate */
	if (!node.made) {
		if (node.flags & NODE_CHECK) {
			return -MATE + app->game.board->ply;
		} else {
			return 0;
		}
	}

	if (alpha != palpha) {
		/* store this as an exact, since we beat alpha */
		store_hash(&app->hash, app->game.board, node.best, highest, HASH_EXACT, depth);
	} else {
		/* store the current alpha */
		store_hash(&app->hash, app->game.board, node.best, alpha, HASH_ALPHA, depth);
	}

	return alpha;
}