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; }
void print_result() { int i; /* is there a legal move? */ for (i = 0; i < first_move[1]; ++i) if (makemove(gen_dat[i].m.b)) { takeback(); break; } if (i == first_move[1]) { if (in_check(side)) { if (side == LIGHT) printf("0-1 {Black mates}\n"); else printf("1-0 {White mates}\n"); } else printf("1/2-1/2 {Stalemate}\n"); } else if (reps() == 3) printf("1/2-1/2 {Draw by repetition}\n"); else if (fifty >= 100) printf("1/2-1/2 {Draw by fifty move rule}\n"); }
/** extend_move(): Calculates the search extension to be awarded to the given move. This is based on various information such as whether the move checks, if it is a threat or not, etc. This also determines whether a move should be reduced or not. Created 072007; last modified 081808 **/ int extend_move(SEARCH_BLOCK *sb) { int extension; int lmr_moves; /* First, determine if the move should be extended (rather than reduced). */ extension = 0; if (!(sb->move & MOVE_NOEXTEND)) { /* check extension */ if (in_check()) { extension += zct->check_extension; zct->check_extensions_done++; } /* one-reply extension */ if (sb->check && sb->last_move - sb->first_move == 1) { extension += zct->one_rep_extension; zct->one_rep_extensions_done++; } /* pawn to 7th extension */ if (board.piece[MOVE_TO(sb->move)] == PAWN && RANK_OF(SQ_FLIP_COLOR(MOVE_TO(sb->move), board.side_ntm)) == RANK_7) { extension += zct->passed_pawn_extension; zct->passed_pawn_extensions_done++; } extension = MIN(PLY, extension); } /* Now try to reduce the move. If the move is marked as not reducible, or if the depth is too low, don't bother reducing it. */ if (extension > 0 || sb->depth < 2 * PLY || (sb->move & MOVE_NOREDUCE)) return extension; lmr_moves = 0; switch (sb->node_type) { case NODE_PV: lmr_moves = 5; break; case NODE_CUT: lmr_moves = 2; break; case NODE_ALL: lmr_moves = 3; break; } /* Most LMR conditions are already covered in the move scoring routines. The only "dynamic" factor is the count of moves. */ if (sb->moves > lmr_moves) { sb->move |= MOVE_ISREDUCED; extension -= PLY; } return extension; }
/* print_result() checks to see if the game is over */ void print_result() { int i; /* is there a legal move? */ for (i = 0; i < first_move[1]; ++i) if (makemove(gen_dat[i].m.b)) { takeback(); break; } if (i == first_move[1]) { if (in_check(side)) { if (side == LIGHT) gprintf("Black mates"); else gprintf("White mates"); } else gprintf("Stalemate"); } else if (reps() == 3) gprintf("Draw by repetition"); else if (fifty >= 100) gprintf("Draw by fifty move rule"); }
void move_set_attr(board_t *b, move_t *move) { int check, mated; board_t board = *b; if (move_is_capture(b, move)) move->type = CAPTURE; else move->type = NORMAL; if (PIECE(b->square[move->source]) == KING) { int hor = move->destination % 8 - move->source % 8; if (hor > 1) move->type = KINGSIDE_CASTLE; else if (hor < -1) move->type = QUEENSIDE_CASTLE; } make_move(&board, move); check = in_check(&board, board.turn); mated = is_mated(&board, board.turn); if (check && mated) move->state = MOVE_CHECKMATE; else if (check) move->state = MOVE_CHECK; else if (mated) move->state = MOVE_STALEMATE; else move->state = MOVE_NORMAL; }
void record_a_move(int x, int y, int to_x, int to_y, int captures_only, int depth) { //printf("record(%d,%d,%d,%d,%d,%d)\n",x,y,to_x,to_y,captures_only,depth); if (captures_only && board.cells[to_x][to_y] == 0) return; if (board.cells[x][y]) ++attacked_squares[depth][to_x][to_y]; if (attacked_squares_only == 0) { int check; moves[depth][move_counter[depth]].x = x; moves[depth][move_counter[depth]].y = y; moves[depth][move_counter[depth]].piece = board.cells[x][y]; moves[depth][move_counter[depth]].to_x = to_x; moves[depth][move_counter[depth]].to_y = to_y; moves[depth][move_counter[depth]].captured_piece = board.cells[to_x][to_y]; make_move(&moves[depth][move_counter[depth]]); board.side = !board.side; // these switches are dumb check = in_check(depth); board.side = !board.side; unmake_move(&moves[depth][move_counter[depth]]); if (!check) ++move_counter[depth]; } }
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; }
int move_is_valid(board_t *b, move_t *move) { board_t board = *b; if (!move_is_semi_valid(&board, move)) return 0; make_move(&board, move); return !in_check(&board, b->turn); }
void record_a_spawn(piece, to_x, to_y, depth) { int check; moves[depth][move_counter[depth]].x = -1; moves[depth][move_counter[depth]].y = -1; moves[depth][move_counter[depth]].piece = piece; moves[depth][move_counter[depth]].to_x = to_x; moves[depth][move_counter[depth]].to_y = to_y; moves[depth][move_counter[depth]].captured_piece = 0; make_move(&moves[depth][move_counter[depth]]); board.side = !board.side; // these switches are dumb check = in_check(depth); board.side = !board.side; unmake_move(&moves[depth][move_counter[depth]]); if (!check) ++move_counter[depth]; }
void search(board *b, int ply) { clear_stats(); // Stats for search sstats.depth = ply; // Start timer for the search struct timeval t1, t2; gettimeofday(&t1, NULL); int result; if (use_mtd_f) result = mtd_f(b, ply); else { coord king_loc = b->black_to_move ? b->black_king : b->white_king; bool side_to_move_in_check = in_check(b, king_loc.col, king_loc.row, b->black_to_move); result = abq(b, NEG_INFINITY, POS_INFINITY, ply, 0, true, side_to_move_in_check); } gettimeofday(&t2, NULL); // Compute and print the elapsed time in millisec double search_millisec = (t2.tv_sec - t1.tv_sec) * 1000.0; // sec to ms search_millisec += (t2.tv_usec - t1.tv_usec) / 1000.0; // us to ms sstats.time = search_millisec; }
/* Let's make the iPod think about ;-) */ void computer_play(void) { if (!is_mini) { pz_draw_header("computer play"); } /* think about the move and make it */ //sleep(1); printf("think\n"); think(2); //sleep(1); if (!pv[0][0].u) { printf("No legal moves\n"); if (!is_mini) { pz_draw_header ("No legal moves"); } } sprintf(cpu_move,"%s", move_str(pv[0][0].b)); makemove(pv[0][0].b); ply = 0; gen_moves(); if (in_check(LIGHT)) { if (is_mini) { draw_message(" ", "Check"); } else { pz_draw_header("Check"); } } else { if (is_mini) { draw_message(" ", "Play"); } else { pz_draw_header("Your Turn"); } } }
/* Common function for cursor positionning */ void new_cursor_position(void) { char temp1[5], temp2[12]; print_board(); if (in_check(LIGHT)) { if (sel) { sprintf(temp2, "Check! %c%c-%c%c",oldx,oldy,xcoord,ycoord); } else { sprintf(temp2, "Check! %c%c-",xcoord,ycoord); } if (!is_mini) { pz_draw_header(temp2); } } else { if(sel) { sprintf(temp1,"%c%c-%c%c",oldx,oldy,xcoord,ycoord); } else { sprintf(temp1,"%c%c-",xcoord,ycoord); } if (is_mini) { draw_message(temp1," "); } else { pz_draw_header(temp1); } } print_result(); printf(temp1); printf("\n"); }
int mtd_f(board *board, int ply) { int g; // First guess of evaluation evaluation stored; tt_get(board, &stored); // Use last pass in Transposition Table if (!e_eq(stored, no_eval)) g = stored.score; // If not present, use static evaluation as guess else { g = evaluate(board); if (board->black_to_move) g = -g; } int upper_bound = POS_INFINITY; int lower_bound = NEG_INFINITY; coord king_loc = board->black_to_move ? board->black_king : board->white_king; bool side_to_move_in_check = in_check(board, king_loc.col, king_loc.row, board->black_to_move); while (lower_bound < upper_bound) { if (search_terminate_requested) return 0; int beta; if (g == lower_bound) beta = g+1; else beta = g; g = abq(board, beta-1, beta, ply, 0, true, side_to_move_in_check); if (g < beta) upper_bound = g; else lower_bound = g; } return g; }
void CheckBadFlow(bool reset) { move_s hismoves[MOVE_BUFF]; move_s ourmoves[MOVE_BUFF]; int his_num_moves, our_num_moves, j, i, ic, icc; bool othermove = FALSE; int pawnmates = FALSE, knightmates = FALSE, bishopmates = FALSE, rookmates = FALSE, queenmates = FALSE; static int pawnmated = FALSE, knightmated = FALSE, bishopmated = FALSE, rookmated = FALSE, queenmated = FALSE; bool pawnwarn = FALSE, knightwarn = FALSE, bishopwarn = FALSE, rookwarn = FALSE, queenwarn = FALSE; if (reset) { pawnmated = FALSE; knightmated = FALSE; bishopmated = FALSE; rookmated = FALSE; queenmated = FALSE; return; } ic = in_check(); if (!holding[!white_to_move][(white_to_move ? wpawn : bpawn)]) { DropaddHolding((white_to_move ? wpawn : bpawn) , !white_to_move); gen(&hismoves[0]); his_num_moves = numb_moves; for(i = 0; (i < his_num_moves) && (pawnmates == FALSE); i++) { make(&hismoves[0], i); if (check_legal(&hismoves[0], i, ic)) { pawnmates = CANCEL_THRESH; icc = in_check(); gen(&ourmoves[0]); our_num_moves = numb_moves; for (j = 0; (j < our_num_moves) && (pawnmates != FALSE); j++) { make(&ourmoves[0], j); if (check_legal(&ourmoves[0], j, icc)) pawnmates = FALSE; unmake(&ourmoves[0], j); } } unmake(&hismoves[0], i); } DropremoveHolding((white_to_move ? wpawn : bpawn), !white_to_move); } if (!holding[!white_to_move][(white_to_move ? wknight : bknight)]) { DropaddHolding((white_to_move ? wknight : bknight) , !white_to_move); gen(&hismoves[0]); his_num_moves = numb_moves; for(i = 0; (i < his_num_moves) && (knightmates == FALSE); i++) { make(&hismoves[0], i); if (check_legal(&hismoves[0], i, ic)) { knightmates = CANCEL_THRESH; icc = in_check(); gen(&ourmoves[0]); our_num_moves = numb_moves; for (j = 0; (j < our_num_moves) && (knightmates != FALSE); j++) { make(&ourmoves[0], j); if (check_legal(&ourmoves[0], j, icc)) knightmates = FALSE; unmake(&ourmoves[0], j); } } unmake(&hismoves[0], i); } DropremoveHolding((white_to_move ? wknight : bknight), !white_to_move); } if (!holding[!white_to_move][(white_to_move ? wbishop : bbishop)]) { DropaddHolding((white_to_move ? wbishop : bbishop) , !white_to_move); gen(&hismoves[0]); his_num_moves = numb_moves; for(i = 0; (i < his_num_moves) && (bishopmates == FALSE); i++) { make(&hismoves[0], i); if (check_legal(&hismoves[0], i, ic)) { bishopmates = CANCEL_THRESH; icc = in_check(); gen(&ourmoves[0]); our_num_moves = numb_moves; for (j = 0; (j < our_num_moves) && (bishopmates != FALSE); j++) { make(&ourmoves[0], j); if (check_legal(&ourmoves[0], j, icc)) bishopmates = FALSE; unmake(&ourmoves[0], j); } } unmake(&hismoves[0], i); } DropremoveHolding((white_to_move ? wbishop : bbishop), !white_to_move); } if (!holding[!white_to_move][(white_to_move ? wrook : brook)]) { DropaddHolding((white_to_move ? wrook : brook) , !white_to_move); gen(&hismoves[0]); his_num_moves= numb_moves; for(i = 0; (i < his_num_moves) && (rookmates == FALSE); i++) { make(&hismoves[0], i); if (check_legal(&hismoves[0], i, ic)) { rookmates = CANCEL_THRESH; icc = in_check(); gen(&ourmoves[0]); our_num_moves = numb_moves; for (j = 0; (j < our_num_moves) && (rookmates != FALSE); j++) { make(&ourmoves[0], j); if (check_legal(&ourmoves[0], j, icc)) rookmates = FALSE; unmake(&ourmoves[0], j); } } unmake(&hismoves[0], i); } DropremoveHolding((white_to_move ? wrook : brook), !white_to_move); } if (!holding[!white_to_move][(white_to_move ? wqueen : bqueen)]) { DropaddHolding((white_to_move ? wqueen : bqueen) , !white_to_move); gen(&hismoves[0]); his_num_moves= numb_moves; for(i = 0; (i < his_num_moves) && (queenmates == FALSE); i++) { make(&hismoves[0], i); if (check_legal(&hismoves[0], i, ic)) { queenmates = CANCEL_THRESH; icc = in_check(); gen(&ourmoves[0]); our_num_moves = numb_moves; for (j = 0; (j < our_num_moves) && (queenmates != FALSE); j++) { make(&ourmoves[0], j); if (check_legal(&ourmoves[0], j, icc)) queenmates = FALSE; unmake(&ourmoves[0], j); } } unmake(&hismoves[0], i); } DropremoveHolding((white_to_move ? wqueen : bqueen), !white_to_move); } /* order in which we tell things is important if we partner ourselves */ /* only update if changed */ if (pawnmates != pawnmated) { if (pawnmates == CANCEL_THRESH) pawnwarn = TRUE; else if (pawnmates == 0 && pawnmated == 0) { printf("tellics ptell p doesn't mate me anymore\n"); othermove = TRUE; } } if (knightmates != knightmated) { if (knightmates == CANCEL_THRESH) knightwarn = TRUE; else if (knightmates == 0 && knightmated == 0) { printf("tellics ptell n doesn't mate me anymore\n"); othermove = TRUE; } } if (bishopmates != bishopmated) { if (bishopmates == CANCEL_THRESH) bishopwarn = TRUE; else if (bishopmates == 0 && bishopmated == 0) { printf("tellics ptell b doesn't mate me anymore\n"); othermove = TRUE; } } if (rookmates != rookmated) { if (rookmates == CANCEL_THRESH) rookwarn = TRUE; else if (rookmates == 0 && rookmated == 0) { printf("tellics ptell r doesn't mate me anymore\n"); othermove = TRUE; } } if (queenmates != queenmated) { if (queenmates == CANCEL_THRESH) queenwarn = TRUE; else if (queenmates == 0 && queenmated == 0) { printf("tellics ptell q doesn't mate me anymore\n"); othermove = TRUE; } } if (pawnwarn) printf("tellics ptell ---p\n"); if (knightwarn) printf("tellics ptell ---n\n"); if (bishopwarn) printf("tellics ptell ---b\n"); if (rookwarn) printf("tellics ptell ---r\n"); if (queenwarn) printf("tellics ptell ---q\n"); /* if other sjeng had to sit because of piece-loss, he may be able to go now */ if (piecedead && othermove) { piecedead = FALSE; printf("tellics ptell x\n"); printf("tellics ptell go\n"); go_fast = FALSE; } (pawnmates) ? (pawnmated = pawnmates) : (pawnmated--); (bishopmates) ? (bishopmated = bishopmates) : (bishopmated--); (rookmates) ? (rookmated = rookmates) : (rookmated--); (queenmates) ? (queenmated = queenmates) : (queenmated--); (knightmates) ? (knightmated = knightmates) : (knightmated--); return; }
void E_scene_main_game::update_result() { /* check for draw by the 50 move rule: */ if (fifty > 100) { result = draw_by_fifty; return; } else if (is_draw ()) { result = draw_by_rep; return; } move_s moves[MOVE_BUFF]; int num_moves, i, ep_temp; d_long temp_hash; ep_temp = ep_square; temp_hash = cur_pos; num_moves = 0; gen (&moves[0], &num_moves); // for all possible moves for all pieces for (i = 0; i < num_moves; i++) { // if the move is from the side of the current player bool color=moves[i].from%2; if (current_color==WHITE) color=!color; // if the selected piece is not owned by the current player, continue if (!color) continue; // test if the move is legal make (&moves[0], i); bool legal=check_legal (&moves[0], i); unmake (&moves[0], i); ep_square = ep_temp; cur_pos = temp_hash; // if we find a legal move, game can continue if (legal) return; } // if we are here, it means that there is no // move avaible for the current player if (in_check ()) { if (white_to_move == 1) result = white_is_mated; else result = black_is_mated; } else { result = stalemate; } }
/* * in_find(): Find a match for the host, user, domain spec */ static int in_find(char *ypdom, struct stringlist *sl, char *grp, const char *host, const char *user, const char *domain) { char *line, *p; int i; struct netgroup *ng; char *name; #ifdef DEBUG_NG (void) fprintf(stderr, "in_find(%s)\n", grp); #endif /* check for cycles */ if (_ng_sl_find(sl, grp) != NULL) { _warnx("netgroup: Cycle in group `%s'", grp); free(grp); return 0; } if (_ng_sl_add(sl, grp) == -1) { free(grp); return 0; } /* Lookup this netgroup */ if (!lookup(ypdom, grp, &line, _NG_KEYBYNAME)) return 0; p = line; for (;;) { switch (_ng_parse(&p, &name, &ng)) { case _NG_NONE: /* Done with the line */ free(line); return 0; case _NG_GROUP: /* new netgroup */ i = in_check(host, user, domain, ng); if (ng->ng_host != NULL) free(ng->ng_host); if (ng->ng_user != NULL) free(ng->ng_user); if (ng->ng_domain != NULL) free(ng->ng_domain); free(ng); if (i) { free(line); return 1; } break; case _NG_NAME: /* netgroup name */ if (in_find(ypdom, sl, name, host, user, domain)) { free(line); return 1; } break; case _NG_ERROR: free(line); return 0; } } }
BOOL makemove(move_bytes m) { /* test to see if a castle move is legal and move the rook (the king is moved with the usual move code later) */ if (m.bits & 2) { int from, to; if (in_check(side)) return FALSE; switch (m.to) { case 62: if (color[F1] != EMPTY || color[G1] != EMPTY || attack(F1, xside) || attack(G1, xside)) return FALSE; from = H1; to = F1; break; case 58: if (color[B1] != EMPTY || color[C1] != EMPTY || color[D1] != EMPTY || attack(C1, xside) || attack(D1, xside)) return FALSE; from = A1; to = D1; break; case 6: if (color[F8] != EMPTY || color[G8] != EMPTY || attack(F8, xside) || attack(G8, xside)) return FALSE; from = H8; to = F8; break; case 2: if (color[B8] != EMPTY || color[C8] != EMPTY || color[D8] != EMPTY || attack(C8, xside) || attack(D8, xside)) return FALSE; from = A8; to = D8; break; default: /* shouldn't get here */ from = -1; to = -1; break; } color[to] = color[from]; piece[to] = piece[from]; color[from] = EMPTY; piece[from] = EMPTY; } /* back up information so we can take the move back later. */ hist_dat[hply].m.b = m; hist_dat[hply].capture = piece[(int)m.to]; hist_dat[hply].castle = castle; hist_dat[hply].ep = ep; hist_dat[hply].fifty = fifty; ++ply; ++hply; /* update the castle, en passant, and fifty-move-draw variables */ castle &= castle_mask[(int)m.from] & castle_mask[(int)m.to]; if (m.bits & 8) { if (side == LIGHT) ep = m.to + 8; else ep = m.to - 8; } else ep = -1; if (m.bits & 17) fifty = 0; else ++fifty; /* move the piece */ color[(int)m.to] = side; if (m.bits & 32) piece[(int)m.to] = m.promote; else piece[(int)m.to] = piece[(int)m.from]; color[(int)m.from] = EMPTY; piece[(int)m.from] = EMPTY; /* erase the pawn if this is an en passant move */ if (m.bits & 4) { if (side == LIGHT) { color[m.to + 8] = EMPTY; piece[m.to + 8] = EMPTY; } else { color[m.to - 8] = EMPTY; piece[m.to - 8] = EMPTY; } } /* switch sides and test for legality (if we can capture the other guy's king, it's an illegal position and we need to take the move back) */ side ^= 1; xside ^= 1; if (in_check(xside)) { takeback(); return FALSE; } return TRUE; }
// Unified alpha-beta and quiescence search int abq(board *b, int alpha, int beta, int ply, int centiply_extension, bool allow_extensions, bool side_to_move_in_check) { if (search_terminate_requested) return 0; // Check for search termination int alpha_orig = alpha; // For use in later TT storage // Retrieve the value from the transposition table, if appropriate evaluation stored; tt_get(b, &stored); if (!e_eq(stored, no_eval) && stored.depth >= ply && use_ttable) { if (stored.type == qexact || stored.type == exact) return stored.score; if (stored.type == qlowerbound || stored.type == lowerbound) alpha = max(alpha, stored.score); else if (stored.type == qupperbound || stored.type == upperbound) beta = min(beta, stored.score); if (alpha >= beta) return stored.score; } // Futility pruning: enter quiescence early if the node is futile if (use_futility_pruning && !side_to_move_in_check && ply == 1) { if (relative_evaluation(b) + frontier_futility_margin < alpha) ply = 0; } else if (use_futility_pruning && !side_to_move_in_check && ply == 2) { if (relative_evaluation(b) + prefrontier_futility_margin < alpha) ply = 0; } bool quiescence = (ply <= 0); // Generate all possible moves for the quiscence search or normal search, and compute the // static evaluation if applicable. move *moves = NULL; int num_available_moves = 0; if (quiescence) moves = board_moves(b, &num_available_moves, true); // Generate only captures else moves = board_moves(b, &num_available_moves, false); // Generate all moves if (quiescence && !use_qsearch) { free(moves); return relative_evaluation(b); // If qsearch is turned off } // Abort if the quiescence search is too deep (currently 45 plies) if (ply < -quiesce_ply_cutoff) { sstats.qnode_aborts++; free(moves); return relative_evaluation(b); } int quiescence_stand_pat; // Allow the quiescence search to generate cutoffs if (quiescence) { quiescence_stand_pat = relative_evaluation(b); alpha = max(alpha, quiescence_stand_pat); if (alpha >= beta) { free(moves); return quiescence_stand_pat; } } else if (!e_eq(stored, no_eval) && use_tt_move_hueristic) { assert(is_legal_move(b, stored.best)); // TODO // For non-quiescence search, use the TT entry as a hueristic moves[num_available_moves] = stored.best; num_available_moves++; } // Update search stats if (quiescence) sstats.qnodes_searched++; else sstats.nodes_searched++; // Search hueristic: sort exchanges using MVV-LVA if (quiescence && mvvlva) nlopt_qsort_r(moves, num_available_moves, sizeof(move), b, &capture_move_comparator); // Search extensions bool no_more_extensions = false; // Extend the search if we are in check //coord king_loc = b->black_to_move ? b->black_king : b->white_king; bool currently_in_check = side_to_move_in_check; //in_check(b, king_loc.col, king_loc.row, b->black_to_move); if (check_extend && currently_in_check && ply <= check_extend_threshold && !quiescence && allow_extensions) { // only extend in shallow non-quiescence situations centiply_extension += check_extension_centiply; no_more_extensions = true; } // Process any extensions if (allow_extensions && centiply_extension >= 100) { centiply_extension -= 100; ply += 1; } else if (allow_extensions && centiply_extension <= -100) { centiply_extension += 100; ply -= 1; } if (no_more_extensions) allow_extensions = false; // Only allow one check extension move best_move_yet = no_move; int best_score_yet = NEG_INFINITY; int num_moves_actually_examined = 0; // We might end up in checkmate //for (int iterations = 0; iterations < 2; iterations++) { // ABDADA iterations for (int i = num_available_moves - 1; i >= 0; i--) { // Iterate backwards to match MVV-LVA sort order /*int claimed_node_id = -1; if (i != num_available_moves - 1 && iterations == 1) { // Skip redundant young brothers on the first pass if (!tt_try_to_claim_node(b, &claimed_node_id)) continue; // Skip the node if it is already being searched } else tt_always_claim_node(b, &claimed_node_id);*/ apply(b, moves[i]); bool we_moved_into_check; // Choose the more efficient version if possible // If we were already in check, we need to do the expensive search if (side_to_move_in_check) { coord king_loc = b->black_to_move ? b->white_king : b->black_king; // for side that just moved we_moved_into_check = in_check(b, king_loc.col, king_loc.row, !(b->black_to_move)); } else we_moved_into_check = puts_in_check(b, moves[i], !b->black_to_move); // Never move into check if (we_moved_into_check) { unapply(b, moves[i]); //tt_unclaim_node(claimed_node_id); continue; } bool opponent_in_check = puts_in_check(b, moves[i], b->black_to_move); /*coord opp_king_loc = b->black_to_move ? b->black_king : b->white_king; bool opponent_in_check = in_check(b, opp_king_loc.col, opp_king_loc.row, (b->black_to_move));*/ int score = -abq(b, -beta, -alpha, ply - 1, centiply_extension, allow_extensions, opponent_in_check); num_moves_actually_examined++; unapply(b, moves[i]); if (score > best_score_yet) { best_score_yet = score; best_move_yet = moves[i]; } alpha = max(alpha, best_score_yet); if (alpha >= beta) { //tt_unclaim_node(claimed_node_id); break; } //tt_unclaim_node(claimed_node_id); } //} free(moves); // We are done with the array // We have no available moves (or captures) that don't leave us in check // This means checkmate or stalemate in normal search // It might mean no captures are available in quiescence search if (num_moves_actually_examined == 0) { if (quiescence) return quiescence_stand_pat; // TODO: qsearch doesn't understand stalemate or checkmate // This seems paradoxical, but the +1 is necessary so we pick some move in case of checkmate if (currently_in_check) return NEG_INFINITY + 1; // checkmate else return 0; // stalemate } if (quiescence && best_score_yet < quiescence_stand_pat) return quiescence_stand_pat; // TODO experimental stand pat if (search_terminate_requested) return 0; // Search termination preempts tt_put // Record the selected move in the transposition table evaltype type; if (best_score_yet <= alpha_orig) type = (quiescence) ? qupperbound : upperbound; else if (best_score_yet >= beta) type = (quiescence) ? qlowerbound : lowerbound; else type = (quiescence) ? qexact : exact; evaluation eval = {.best = best_move_yet, .score = best_score_yet, .type = type, .depth = ply}; tt_put(b, eval); return best_score_yet; }
int generate_moves(Move *movelist) { int horizontal2 = turn_to_move == WHITE? 8 : 3; int horizontal7 = turn_to_move == WHITE? 3 : 8; int direction_of_pawns = turn_to_move == WHITE? -10: 10; int captures_of_pawns[2] = {direction_of_pawns + 1, direction_of_pawns - 1}; int place_of_king = turn_to_move == WHITE? place_of_white_king: place_of_black_king; int n = 0; int king_castling, queen_castling; if(turn_to_move == WHITE) { king_castling = ply->castlings & K_castling; queen_castling = ply->castlings & Q_castling; } else { king_castling = ply->castlings & k_castling; queen_castling = ply->castlings & q_castling; } int is_in_check = in_check(turn_to_move); for(int i = 0; i < 8; i += 1) { int i_move = moves_of_king[i]; int tmp = place_of_king + i_move; if(board[tmp] == EMPTY || get_color(board[tmp]) == not_turn_to_move) { int i_move_is_possible = 0; Move tmp_move = create_move(place_of_king, tmp, board[tmp], 0); make_move(tmp_move); if(!in_check(not_turn_to_move)) { i_move_is_possible = 1; movelist[n] = tmp_move; n += 1; } unmake_move(tmp_move); if(is_in_check || !i_move_is_possible || board[tmp] != EMPTY) continue; if(i_move == 1 && king_castling && board[place_of_king + 2] == EMPTY) { movelist[n] = create_move(place_of_king, place_of_king + 2, 0, 0); n += 1; } if(i_move == -1 && queen_castling && board[place_of_king - 2] == EMPTY && board[place_of_king - 3] == EMPTY) { movelist[n] = create_move(place_of_king, place_of_king - 2, 0, 0); n += 1; } } } if(ply->en_passant) { for(int i = 0; i < 2; i += 1) { int tmp = ply->en_passant - captures_of_pawns[i]; if(board[tmp] == create_figure(turn_to_move, PAWN)) { movelist[n] = create_move(tmp, ply->en_passant, 0, 0); n += 1; } } } for(int i64 = 0; i64 < 64; i64 += 1) { int current_cell = board64[i64]; int figure = board[current_cell]; if(get_color(figure) != turn_to_move) continue; switch(get_value(figure)) { case QUEEN: for(int i = 0; i < 8; i += 1) { int inc = directions_of_queen[i]; int x = current_cell + inc; while(board[x] == EMPTY) { movelist[n] = create_move(current_cell, x, 0, 0); n += 1; x += inc; } if(get_color(board[x]) == not_turn_to_move ) { movelist[n] = create_move(current_cell, x, board[x], 0); n += 1; } } break; case ROOK: for(int i = 0; i < 4; i += 1) { int inc = directions_of_rook[i]; int x = current_cell + inc; while(board[x] == EMPTY) { movelist[n] = create_move(current_cell, x, 0, 0); n += 1; x += inc; } if(get_color(board[x]) == not_turn_to_move) { movelist[n] = create_move(current_cell, x, board[x], 0); n += 1; } } break; case BISHOP: for(int i = 0; i < 4; i += 1) { int inc = directions_of_bishop[i]; int x = current_cell + inc; while(board[x] == EMPTY) { movelist[n] = create_move(current_cell, x, 0, 0); n += 1; x += inc; } if(get_color(board[x]) == not_turn_to_move) { movelist[n] = create_move(current_cell, x, board[x], 0); n += 1; } } break; case KNIGHT: for(int i = 0; i < 8; i += 1) { int tmp = current_cell + moves_of_knight[i]; if(board[tmp] == EMPTY || get_color(board[tmp]) == not_turn_to_move) { movelist[n] = create_move(current_cell, tmp, board[tmp], 0); n += 1; } } break; case PAWN: ;int tmp = current_cell + direction_of_pawns; if(board[tmp] == EMPTY) { if(current_cell/10 == horizontal7) { for(int j = 0; j < 4; j += 1) { movelist[n] = create_move(current_cell, tmp, 0, create_figure(turn_to_move, turn_figures[j])); n += 1; } } else { movelist[n] = create_move(current_cell, tmp, 0, 0); n += 1; } tmp += direction_of_pawns; if(board[tmp] == EMPTY && current_cell/10 == horizontal2) { movelist[n] = create_move(current_cell, tmp, 0, 0); n += 1; } } for(int i = 0; i < 2; i += 1) { int tmp = current_cell + captures_of_pawns[i]; if(get_color(board[tmp]) == not_turn_to_move) { if(current_cell/10 == horizontal7) { for(int j = 0; j < 4; j += 1) { movelist[n] = create_move(current_cell, tmp, board[tmp], create_figure(turn_to_move, turn_figures[j])); n += 1; } } else { movelist[n] = create_move(current_cell, tmp, board[tmp], 0); n += 1; } } } } } for(int i = 0; i < n; i += 1) { Move i_move = movelist[i]; make_move(i_move); if(in_check(not_turn_to_move)) { movelist[i] = movelist[n - 1]; n -= 1; i -= 1; } unmake_move(i_move); } return n; }
int generate_captures(Move *movelist) { int horizontal2 = turn_to_move == WHITE? 8 : 3; int horizontal7 = turn_to_move == WHITE? 3 : 8; int direction_of_pawns = turn_to_move == WHITE? -10: 10; int captures_of_pawns[2] = {direction_of_pawns + 1, direction_of_pawns - 1}; int place_of_king = turn_to_move == WHITE? place_of_white_king: place_of_black_king; int n = 0; for(int i = 0; i < 8; i += 1) { int tmp = place_of_king + moves_of_king[i]; if(get_color(board[tmp]) == not_turn_to_move) { movelist[n] = create_move(place_of_king, tmp, board[tmp], 0); n += 1; } } for(int i64 = 0; i64 < 64; i64 += 1) { int current_cell = board64[i64]; int figure = board[current_cell]; if(get_color(figure) != turn_to_move) continue; switch(get_value(figure)) { case QUEEN: for(int i = 0; i < 8; i += 1) { int inc = directions_of_queen[i]; int x = current_cell + inc; while(board[x] == EMPTY) x += inc; if(get_color(board[x]) == not_turn_to_move) { movelist[n] = create_move(current_cell, x, board[x], 0); n += 1; } } break; case ROOK: for(int i = 0; i < 4; i += 1) { int inc = directions_of_rook[i]; int x = current_cell + inc; while(board[x] == EMPTY) x += inc; if(get_color(board[x]) == not_turn_to_move) { movelist[n] = create_move(current_cell, x, board[x], 0); n += 1; } } break; case BISHOP: for(int i = 0; i < 4; i += 1) { int inc = directions_of_bishop[i]; int x = current_cell + inc; while(board[x] == EMPTY) x += inc; if(get_color(board[x]) == not_turn_to_move) { movelist[n] = create_move(current_cell, x, board[x], 0); n += 1; } } break; case KNIGHT: for(int i = 0; i < 8; i += 1) { int tmp = current_cell + moves_of_knight[i]; if(get_color(board[tmp]) == not_turn_to_move) { movelist[n] = create_move(current_cell, tmp, board[tmp], 0); n += 1; } } break; case PAWN: for(int i = 0; i < 2; i += 1) { int tmp = current_cell + captures_of_pawns[i]; if(get_color(board[tmp]) == not_turn_to_move) { movelist[n] = create_move(current_cell, tmp, board[tmp], 0); n += 1; } } } } for(int i = 0; i < n; i += 1) { Move i_move = movelist[i]; make_move(i_move); if(in_check(not_turn_to_move)) { movelist[i] = movelist[n - 1]; n -= 1; i -= 1; } unmake_move(i_move); } return n; }
int is_legal_move(int x1, int y1, int x2, int y2) { square backup; double slope, distance; int a, b; int putting_yourself_in_check; register int xt, yt; const double infinity = (double)(BOARD_SIZE - 0) / (double)(1 - 0); const int moving_piece_color = get_player_by_square(x1, y1); const int target_piece_color = get_player_by_square(x2, y2); /* * If either the starting or ending squares lie outside the legal boundary of * the board, then obviously the move cannot be legal. */ if (x1 < 0 || x1 >= BOARD_SIZE) return 0; if (y1 < 0 || y1 >= BOARD_SIZE) return 0; if (x2 < 0 || x2 >= BOARD_SIZE) return 0; if (y2 < 0 || y2 >= BOARD_SIZE) return 0; if (board[y1][x1] == BLANK_SQUARE) return 0; /* can move to no squares, from an empty square */ if (game_state.player_turn != moving_piece_color) return 0; /* White can't command Black pieces, and vice versa. */ if (board[y2][x2] != BLANK_SQUARE) if (target_piece_color == moving_piece_color) return 0; /* Nobody can capture pieces of their own color. */ a = x2 - x1; /* horizontal leg, if any */ b = y2 - y1; /* vertical leg, if any */ distance = sqrt(a*a + b*b); /* vector resultant, if not also hypotenuse */ if (distance == 0) return 0; /* null move */ if (a == 0) slope = (b < 0) ? -infinity : +infinity; else slope = (double)b / (double)a; switch (board[y1][x1]) { default: /* probably intended as a blank square, except not BLANK_SQUARE */ return 0; case WHITE_KING: case BLACK_KING: if (distance == 1) /* up, down, right, left by one */ break; if (distance == sqrt(2)) /* diagonally by one */ break; if (distance == 2 && y2 == y1 && x1 == e) { /* castling from rank e */ if (in_check(game_state.player_turn)) return 0; /* Castling out of check is illegal. */ if (board[y1][x1] == WHITE_KING && y1 == 0) { if (game_state.castling.K && x2 == g) { if (test_King(f, 0) | test_Queen(f, 0) | test_knight(f, 0)) return 0; /* Castling "through" check is illegal. */ if (SQUARE(f1) == BLANK_SQUARE) break; } if (game_state.castling.Q && x2 == c) { if (test_King(d, 0) | test_Queen(d, 0) | test_knight(d, 0)) return 0; if (SQUARE(d1) == BLANK_SQUARE && SQUARE(b1) == BLANK_SQUARE) break; } } else if (board[y1][x1] == BLACK_KING && y1 == BOARD_SIZE - 1) { if (game_state.castling.k && x2 == g) { if (test_King(f, 7) | test_Queen(f, 7) | test_knight(f, 7)) return 0; /* Castling "through" check is illegal. */ if (SQUARE(f8) == BLANK_SQUARE) break; } if (game_state.castling.q && x2 == c) { if (test_King(d, 7) | test_Queen(d, 7) | test_knight(d, 7)) return 0; if (SQUARE(d8) == BLANK_SQUARE && SQUARE(b8) == BLANK_SQUARE) break; } } } return 0; case WHITE_QUEEN: case BLACK_QUEEN: if (slope == -1 || slope == +1) goto bishop_testing; if (slope != -infinity && slope != +infinity && slope != 0) return 0; /* not a legal rook move */ /* Fall through. */ case WHITE_ROOK: case BLACK_ROOK: if (y2 == y1) { if (x2 < x1) /* destination to the left of the rook */ for (xt = x1 - 1; xt > x2; xt--) { if (board[y2][xt] == BLANK_SQUARE) continue; return 0; } else /* destination to the right of the rook */ for (xt = x1 + 1; xt < x2; xt++) { if (board[y2][xt] == BLANK_SQUARE) continue; return 0; } break; } else if (x2 == x1) { if (y2 < y1) /* destination downwards from the rook */ for (yt = y1 - 1; yt > y2; yt--) { if (board[yt][x2] == BLANK_SQUARE) continue; return 0; } else /* destination upwards from the rook */ for (yt = y1 + 1; yt < y2; yt++) { if (board[yt][x2] == BLANK_SQUARE) continue; return 0; } break; } else return 0; case WHITE_BISHOP: case BLACK_BISHOP: bishop_testing: if (slope != +1 && slope != -1) return 0; if (y2 > y1 && x2 > x1) /* quadrant I, range check to upper-right */ for (xt = x1 + 1, yt = y1 + 1; xt < x2 && yt < y2; xt++, yt++) if (board[yt][xt] != BLANK_SQUARE) return 0; if (y2 > y1 && x2 < x1) /* quadrant II, range check to upper-left */ for (xt = x1 - 1, yt = y1 + 1; xt > x2 && yt < y2; xt--, yt++) if (board[yt][xt] != BLANK_SQUARE) return 0; if (y2 < y1 && x2 < x1) /* quadrant III, range check to lower-left */ for (xt = x1 - 1, yt = y1 - 1; xt > x2 && yt > y2; xt--, yt--) if (board[yt][xt] != BLANK_SQUARE) return 0; if (y2 < y1 && x2 > x1) /* quadrant IV, range check to lower-right */ for (xt = x1 + 1, yt = y1 - 1; xt < x2 && yt > y2; xt++, yt--) if (board[yt][xt] != BLANK_SQUARE) return 0; break; case WHITE_KNIGHT: case BLACK_KNIGHT: if (distance == sqrt(5)) break; return 0; case WHITE_PAWN: if (distance == 2) /* optional pawn push from start position */ if (y2 == y1 + 2 && board[y2][x2] == BLANK_SQUARE) if (y1 == -1 + 2 && board[y1 + 1][x1] == BLANK_SQUARE) break; if (distance == 1) /* pawn advances, without capture */ if (y2 == y1 + 1 && board[y2][x2] == BLANK_SQUARE) break; if (distance == sqrt(2)) /* pawn captures */ if (y2 == y1 + 1) if (board[y2][x2] != BLANK_SQUARE || (game_state.en_passant_file == x2 && y2 == BOARD_SIZE - 3)) break; return 0; case BLACK_PAWN: if (distance == 2) /* optional pawn push from start position */ if (y2 == y1 - 2 && board[y2][x2] == BLANK_SQUARE) if (y1 == BOARD_SIZE - 2 && board[y1 - 1][x1] == BLANK_SQUARE) break; if (distance == 1) if (y2 == y1 - 1 && board[y2][x2] == BLANK_SQUARE) break; if (distance == sqrt(2)) /* pawn captures */ if (y2 == y1 - 1) if (board[y2][x2] != BLANK_SQUARE || (game_state.en_passant_file == x2 && y2 == -1 + 3)) break; return 0; } /* * Finally, is the moving player putting himself in check with this move? * This we will test by temporarily executing the move on the board. * * In ancient chess, exposing one's King is a legal move, so this code could * be deleted actually for a theoretically faster analysis. */ backup = board[y2][x2]; board[y2][x2] = board[y1][x1]; board[y1][x1] = BLANK_SQUARE; putting_yourself_in_check = in_check(moving_piece_color); board[y1][x1] = board[y2][x2]; board[y2][x2] = backup; if (putting_yourself_in_check) return 0; return 1; }
int search(int alpha, int beta, int depth) { int i, j, x; BOOL c, f; /* we're as deep as we want to be; call quiesce() to get a reasonable score and return it. */ if (!depth) return quiesce(alpha,beta); ++nodes; /* do some housekeeping every 1024 nodes */ if ((nodes & 1023) == 0) checkup(); pv_length[ply] = ply; /* if this isn't the root of the search tree (where we have to pick a move and can't simply return 0) then check to see if the position is a repeat. if so, we can assume that this line is a draw and return 0. */ if (ply && reps()) return 0; /* are we too deep? */ if (ply >= MAX_PLY - 1) return eval(); if (hply >= HIST_STACK - 1) return eval(); /* are we in check? if so, we want to search deeper */ c = in_check(side); if (c) ++depth; gen(); if (follow_pv) /* are we following the PV? */ sort_pv(); f = FALSE; /* loop through the moves */ for (i = first_move[ply]; i < first_move[ply + 1]; ++i) { sort(i); if (!makemove(gen_dat[i].m.b)) continue; f = TRUE; x = -search(-beta, -alpha, depth - 1); takeback(); if (x > alpha) { /* this move caused a cutoff, so increase the history value so it gets ordered high next time we can search it */ history[(int)gen_dat[i].m.b.from][(int)gen_dat[i].m.b.to] += depth; if (x >= beta) return beta; alpha = x; /* update the PV */ pv[ply][ply] = gen_dat[i].m; for (j = ply + 1; j < pv_length[ply + 1]; ++j) pv[ply][j] = pv[ply + 1][j]; pv_length[ply] = pv_length[ply + 1]; } } /* no legal moves? then we're in checkmate or stalemate */ if (!f) { if (c) return -10000 + ply; else return 0; } /* fifty move draw rule */ if (fifty >= 100) return 0; return alpha; }
/* Aggiorna la scacchiera e le altre strutture dati eseguendo la mossa La verifica di legalita' viene effettuata quando: -la mossa è una mossa di re -il re è sotto scacco -il re è sotto attacco x-ray */ int makemove(const MOVE m, const unsigned char s) { unsigned char from=0; /*da dove parte il pezzo*/ unsigned char to=0; /*dove copiare il pezzo*/ char was=0; /*cosa era il pezzo?*/ char wh=0; unsigned char del=0; /*dove cancellare*/ assert(Name(WHITE,0)==king); assert(Name(BLACK,0)==king); if (Piece(m.ms.to)==king) return 0; /*copia la chiave hash per l'unmake()*/ BHash(N_moves) = Hash; /*copia la castle_exe per l'unmake()*/ BCste(N_moves) = Cste; /*copia la bitboard per l'unmake()*/ BBitb(N_moves, Side) = Bitboard(Side); /*Controlla che l'arrocco sia legale e sposta la torre */ if (m.ms.flag & (M_OO | M_OOO)) { if (tree.check[Side][Ply]) return 0; switch (m.ms.to) { case C8: if (attacked(C8, WHITE) || attacked(D8, WHITE)) return 0; from = A8; to = D8; Cste |= cas_booo; break; case G8: if (attacked(F8, WHITE) || attacked(G8, WHITE)) return 0; from = H8; to = F8; Cste |= cas_boo; break; case C1: if (attacked(C1, BLACK) || attacked(D1, BLACK)) return 0; from = A1; to = D1; Cste |= cas_wooo; break; case G1: if (attacked(F1, BLACK) || attacked(G1, BLACK)) return 0; from = H1; to = F1; Cste |= cas_woo; break; default: puts("Bad castle request."); assert(0); } /*sposta torre aggiornando la chiave hash*/ UpdateHash(from); Piece(to) = Piece(from); Color(to) = Color(from); Plist(to) = Plist(from); Position((Color(to)), (Plist(to))) = to; Piece(from) = no_piece; Color(from) = EMPTY; Plist(from) = no_list; UpdateHash(to); SetBB(Side, to); DelBB(Side, from); } /*sposta il pezzo*/ from = m.ms.from; to = m.ms.to; was = Piece(from); wh = Plist(from); assert(was>=0); assert(was<=king); assert(Name(Side,0)==king); assert(Name(Xside,0)==king); if (wh>0){ assert(Name(Side,wh)!=king); } /*Se la mossa e' una cattura cancella il pezzo dalla casella di arrivo*/ if (m.ms.flag & M_CAP) { del = to; /*se è una cattura en passant aggiustiamo casella*/ if (m.ms.flag & M_ENP) { if (Side) del += DOWN; else del += UP; } BCapP(N_moves) = Piece(del); BCapPos(N_moves) = Plist(del); BCapC(N_moves) = Xside; BBitb(N_moves, Xside) = Bitboard(Xside); Active(Xside, (Plist(del))) = CAPTURED; Piece(del) = no_piece; Color(del) = EMPTY; Plist(del) = no_list; DelBB(Xside, del); } Piece(to) = Piece(from); if (m.ms.piece!=was)Piece(to)=m.ms.piece; Color(to) = Side; Plist(to) = wh; Position((Side), (wh)) = to; Piece(from) = no_piece; Color(from) = EMPTY; Plist(from) = no_list; SetBB(Side, to); DelBB(Side, from); /*effettuato il minimo indispensabile verifica, se opportuno, la legalità della mossa*/ assert(Name(Side,0)==king); assert(Name(Xside,0)==king); if (wh>0){ assert(Name(Side,wh)!=king); } if ( tree.verify[Ply] || tree.check[Side][Ply] || (m.ms.flag & M_KNG) ||(m.ms.flag & M_ENP)) { if (in_check(Side)) { /*unmake() locale*/ Piece(from) = Piece(to); if (Piece(from)!=was)Piece(from)=was; Color(from) = Side; Plist(from) = wh; Position((Side), (wh)) = from; Piece(to) = no_piece; Color(to) = EMPTY; Plist(to) = no_list; Bitboard(Side) = BBitb(N_moves, Side); if (m.ms.flag & M_CAP) { Piece(del) = BCapP(N_moves); Color(del) = BCapC(N_moves); Plist(del) = BCapPos(N_moves); Active((Xside), (Plist(del))) = FREE; Position((Xside), (Plist(del))) = del; Bitboard(Xside) = BBitb(N_moves, Xside); assert(Name(Side,0)==king); assert(Name(Xside,0)==king); if (Plist(del)>0){ assert(Name(Xside,Plist(del))!=king); } } assert(Name(Side,0)==king); assert(Name(Xside,0)==king); if (wh>0){ assert(Name(Side,wh)!=king); } return 0; } } /*Selectivity*/ if (s && ((!in_check(Xside)) || ((m.ms.flag& M_CAP)==0))) { /*unmake() locale*/ Piece(from) = Piece(to); if (Piece(from)!=was)Piece(from)=was; Color(from) = Side; Plist(from) = wh; Position((Side), (wh)) = from; Piece(to) = no_piece; Color(to) = EMPTY; Plist(to) = no_list; Bitboard(Side) = BBitb(N_moves, Side); if (m.ms.flag & M_CAP) { Piece(del) = BCapP(N_moves); Color(del) = BCapC(N_moves); Plist(del) = BCapPos(N_moves); Active((Xside), (Plist(del))) = FREE; Position((Xside), (Plist(del))) = del; Bitboard(Xside) = BBitb(N_moves, Xside); assert(Name(Side,0)==king); assert(Name(Xside,0)==king); if (Plist(del)>0){ assert(Name(Xside,Plist(del))!=king); } } assert(Name(Side,0)==king); assert(Name(Xside,0)==king); if (wh>0){ assert(Name(Side,wh)!=king); } return 0; } /*back up per l'unmakemove() i dati per la cattura e la hash della posizione sono gia' memorizzati*/ tree.history[N_moves].m.mi = m.mi; tree.history[N_moves].cast.castle_right = Cstr; tree.history[N_moves].enp = Enp; tree.history[N_moves].fifty = tree.fifty; tree.history[N_moves].was = was; tree.history[N_moves].material[Side] = Material(Side); tree.history[N_moves].material[Xside] = Material(Xside); BPBitb(N_moves, Side) = Pbitboard(Side); BPBitb(N_moves, Xside) = Pbitboard(Xside); /*aggiorna diritti arrocco*/ UpdateHashCastle(Cstr); Cstr &= castle_mask[b256to64[from]] & castle_mask[b256to64[to]]; UpdateHashCastle(Cstr); Hash ^= zobrist_enp[b256to64[Enp]]; Enp = 0; tree.fifty++; /*se irreversibile...*/ if (m.ms.flag&(M_PAW | M_CAP)) { tree.fifty = 0; if (m.ms.flag&M_DBP) { if (Side) Enp = to + DOWN; else Enp = to + UP; } if (m.ms.flag&M_PAW) { DelPBB(Side, from); if ((m.ms.flag&M_PRO) == 0) SetPBB(Side, to); } if (m.ms.flag&M_CAP) { tree.history[N_moves].del = del; Hash ^= zobrist[Xside][BCapP(N_moves)][b256to64[del]]; Material(Xside) -= piece_value[BCapP(N_moves)]; Num_piece(Xside, BCapP(N_moves))--; Pbitboard(Xside) &= (~(bit_square[del])); } Material(Side) -= piece_value[was]; Material(Side) += piece_value[m.ms.piece]; Num_piece(Side, was)--; Num_piece(Side, m.ms.piece)++; } Hash ^= zobrist_enp[b256to64[Enp]]; Hash ^= zobrist[Side][was][b256to64[from]]; Hash ^= zobrist[Side][m.ms.piece][b256to64[to]]; Hash ^= zobrist_side; Ply++; N_moves++; Change(Side); Change(Xside); assert(Name(WHITE,0)==king); assert(Name(BLACK,0)==king); return 1; }
/* print_result() checks to see if the game is over */ void print_result() { int i; /* is there a legal move? */ for (i = 0; i < first_move[1]; ++i) { if (makemove(gen_dat[i].m.b)) { takeback(); break; } } if (i == first_move[1]) { if (in_check(side)) { if (side == LIGHT) { printf("GR_RGB(0,0,0) mates"); if (is_mini) { draw_message("GR_RGB(0,0,0)","Mates"); } else { pz_draw_header("GR_RGB(0,0,0) mates"); } draw_end('b'); } else { printf("GR_RGB(255,255,255) mates"); if (is_mini) { draw_message("GR_RGB(255,255,255)","Mates"); } else { pz_draw_header("GR_RGB(255,255,255) mates"); } draw_end('w'); } } else { printf("Stalemate"); if (is_mini) { draw_message("Stale","Mate"); } else { pz_draw_header("Stalemate"); } draw_end('d'); } } /* else if (reps() == 3) { printf("Draw by repetition"); if (is_mini == 0) pz_draw_header("Draw by repetition"); draw_end('d'); } */ else if (fifty >= 100) { printf("Draw by fifty move rule"); if (is_mini == 0) { pz_draw_header("Draw : fifty moves"); } draw_end('d'); } }
// 利きのある場所への取れない近接王手からの3手詰め Move Position::weak_mate_n_ply(int ply) const { // 1手詰めであるならこれを返す Move m = mate1ply(); if (m) return m; // 詰まない if (ply <= 1) return MOVE_NONE; Color us = side_to_move(); Color them = ~us; Bitboard around8 = kingEffect(king_square(them)); // const剥がし Position* This = ((Position*)this); StateInfo si; StateInfo si2; // 近接王手で味方の利きがあり、敵の利きのない場所を探す。 for (auto m : MoveList<CHECKS>(*this)) { // 近接王手で、この指し手による駒の移動先に敵の駒がない。 Square to = to_sq(m); if ((around8 & to) #ifndef LONG_EFFECT_LIBRARY // toに利きがあるかどうか。mが移動の指し手の場合、mの元の利きを取り除く必要がある。 && (is_drop(m) ? effected_to(us, to) : (attackers_to(us, to, pieces() ^ from_sq(m)) ^ from_sq(m))) // 敵玉の利きは必ずtoにあるのでそれを除いた利きがあるかどうか。 && (attackers_to(them,to,pieces()) ^ king_square(them)) #else && (is_drop(m) ? effected_to(us, to) : board_effect[us].effect(to) >= 2 || (long_effect.directions_of(us, from_sq(m)) & Effect8::directions_of(from_sq(m), to)) != 0) // 敵玉の利きがあるので2つ以上なければそれで良い。 && (board_effect[them].effect(to) <= 1) #endif ) { if (!legal(m)) continue; ASSERT_LV3(gives_check(m)); This->do_move(m,si,true); ASSERT_LV3(in_check()); // この局面ですべてのevasionを試す for (auto m2 : MoveList<EVASIONS>(*this)) { if (!legal(m2)) continue; // この指し手で逆王手になるなら、不詰めとして扱う if (gives_check(m2)) goto NEXT_CHECK; This->do_move(m2, si2, false); ASSERT_LV3(!in_check()); if (!weak_mate_n_ply(ply-2)) { // 詰んでないので、m2で詰みを逃れている。 This->undo_move(m2); goto NEXT_CHECK; } This->undo_move(m2); } // すべて詰んだ This->undo_move(m); // mによって3手で詰む。 return m; NEXT_CHECK:; This->undo_move(m); } } return MOVE_NONE; }
score_t search_ab(boost::shared_ptr<search_info> proc_info) { if(proc_info->get_abort()) return bad_min_score; // Unmarshall the info struct node_t board = proc_info->board; const int depth = proc_info->depth; if(depth != board.depth) { std::cout << "depth=" << depth << "board.depth=" << board.depth << std::endl; } assert(depth == board.depth); score_t alpha = proc_info->alpha; score_t beta = proc_info->beta; assert(depth >= 0); std::ostringstream strB; print_board(board, strB, true); std::string strBoard = strB.str(); // if we are a leaf node, return the value from the eval() function if (depth == 0) { evaluator ev; DECL_SCORE(s,ev.eval(board, chosen_evaluator),board.hash); return s; } /* if this isn't the root of the search tree (where we have to pick a chess_move and can't simply return 0) then check to see if the position is a repeat. if so, we can assume that this line is a draw and return 0. */ if (board.ply && reps(board)==3) { DECL_SCORE(z,0,board.hash); proc_info->draw = true; return z; } // fifty chess_move draw rule if (board.fifty >= 100) { DECL_SCORE(z,0,board.hash); proc_info->draw = true; return z; } score_t max_val = bad_min_score; score_t p_board = board.p_board; score_t zlo = bad_min_score,zhi = bad_max_score; bool white =board.side == LIGHT; bool entry_found = false; int excess =0; bool exact = false; if (white && board.root_side == LIGHT && db_on && board.ply > 0 && !proc_info->quiescent){ entry_found = dbase.get_transposition_value (board, zlo, zhi, white,p_board,excess,exact,board.depth); int pe = proc_info->excess; if (excess > proc_info->excess){ proc_info->excess = excess; //if (!board.follow_capt && search_method == MTDF) board.follow_capt = true; } else{ //board.follow_depth = 0; } if(entry_found && excess > 0) { assert(depth == board.depth); std::cout << "excess = " << (excess+depth) << std::endl; zhi = bad_max_score; verify(strBoard,board.side,depth+excess,board.castle,board.ep,zlo,bad_max_score); } } if (entry_found){ return zlo; } if(!entry_found) { entry_found = get_transposition_value (board, zlo, zhi); if(!entry_found && db_on && board.side == LIGHT){ entry_found = dbase.get_transposition_value(board,zlo,zhi,white,p_board,excess,true,depth); verify(strBoard,board.side,depth,board.castle,board.ep,zlo,zhi); } } if (entry_found) { if(zlo >= beta) { return zlo; } if(alpha >= zhi) { return zhi; } alpha = max(zlo,alpha); beta = min(zhi,beta); } if(alpha >= beta) { //proc_info->stop=false; //deeper= false; return alpha; } const score_t alpha0 = alpha; std::vector<chess_move> workq; std::vector<chess_move> max_move; gen(workq, board); // Generate the moves #ifdef PV_ON if(!proc_info->use_srand) proc_info->incr = rand(); proc_info->use_srand = false; srand(proc_info->incr); sort_pv(workq, board.depth); // Part of iterative deepening #endif const int worksq = workq.size(); std::vector<boost::shared_ptr<task> > tasks; int j=0; score_t val; bool aborted = false; bool children_aborted = false; // loop through the moves //for (; j < worksq; j++) while(j < worksq) { while(j < worksq) { chess_move g = workq[j++]; boost::shared_ptr<search_info> child_info{new search_info(board)}; bool parallel; if (!aborted && !proc_info->get_abort() && makemove(child_info->board, g)) { parallel = j > 0 && !capture(board,g); boost::shared_ptr<task> t = parallel_task(depth, ¶llel); t->info = child_info; int d = depth - 1; if(!test_alphabeta && d == 0 && capture(board,g)) { d = 1; /* if(!proc_info->quiescent) { t->info->alpha = bad_min_score; t->info->beta = bad_max_score; } */ t->info->quiescent = true; } else if(proc_info->quiescent) { t->info->quiescent = true; } t->info->board.depth = child_info->depth = d; assert(depth >= 0); t->info->alpha = -beta; t->info->beta = -alpha; t->info->mv = g; t->pfunc = search_ab_f; t->start(); tasks.push_back(t); // Control branching if (!parallel) break; /* else if (beta >= max_score*.9) continue; */ else if (tasks.size() < 5) continue; else break; } } When when(tasks); size_t const count = tasks.size(); for(size_t n_=0;n_<count;n_++) { int n = when.any(); boost::shared_ptr<task> child_task = tasks[n]; //assert(child_task.valid()); child_task->join(); boost::shared_ptr<search_info> child_info = child_task->info; tasks.erase(tasks.begin()+n); if(!children_aborted && (aborted || child_info->get_abort())) { for(unsigned int m = 0;m < tasks.size();m++) { tasks[m]->info->set_abort(true); } children_aborted = true; } //child_task->join(); if(child_info->get_abort()) continue; if(child_info->draw) proc_info->draw = true; val = -child_info->result; proc_info->log << " " << child_info->mv << "=" << val; bool found = false; if (child_info->excess > proc_info->excess){ proc_info->excess = child_info->excess; found = true; if (!board.follow_capt){ board.follow_capt = true; } } if (val > max_val || found ) { max_move.clear(); max_move.push_back(child_info->mv); if (val > max_val) max_val = val; if (val > alpha) { alpha = val; #ifdef PV_ON if(!child_info->get_abort()) ;//pv[board.search_depth - 1].set(child_info->mv); #endif if(alpha >= beta) { //aborted = true; j += worksq; continue; } } } else if(val == max_val && proc_info->excess == 0) { max_move.push_back(child_info->mv); } } if(alpha >= beta) { j += worksq; break; } } // no legal moves? then we're in checkmate or stalemate if (max_move.size()==0) { if (in_check(board, board.side)) { DECL_SCORE(s,max_score,board.hash); return s; } else { DECL_SCORE(z,0,board.hash); return z; } } if(db_on) { if (board.ply == 0 || board.depth>0) { assert(max_move.size() != 0); ScopedLock s(cmutex); move_to_make = max_move.at(rand() % max_move.size()); } } bool store = true; if(proc_info->draw) { store = false; } if(proc_info->quiescent) store = false; score_t lo, hi; if(proc_info->excess) { lo = max_val; hi = max_score; //std::cout<<"Max depth: "<<proc_info->excess+depth<<std::endl; store = false; } else if(alpha0 < max_val && max_val < beta) { lo = max_val-1; hi = max_val; } else if(max_val <= alpha0) { hi = max_val; lo = zlo; if(lo == zlo) store = false; } else if (max_val >= beta){ lo = max_val; hi = zhi; if(hi == zhi) store = false; } else { store = false; lo = hi = 0; } if(store && lo > hi) { std::cout << "lo=" << lo << " hi=" << hi << std::endl; abort(); } if(store && db_on) { verify(strBoard,board.side,depth,board.castle,board.ep,lo,hi); } if(store) { //if(board.depth > 1) dbase.add_data(board,lo,hi,white,proc_info->excess); set_transposition_value(board,lo,hi); } return max_val; }