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; }
/** * Use mpmcqs to allow stealing directly from a victim, without waiting for a * response. */ static pony_actor_t* steal(scheduler_t* sched, pony_actor_t* prev) { send_msg(0, SCHED_BLOCK, 0); uint64_t tsc = ponyint_cpu_tick(); pony_actor_t* actor; while(true) { scheduler_t* victim = choose_victim(sched); if(victim == NULL) actor = (pony_actor_t*)ponyint_mpmcq_pop(&inject); else actor = pop_global(victim); if(actor != NULL) { DTRACE3(WORK_STEAL_SUCCESSFUL, (uintptr_t)sched, (uintptr_t)victim, (uintptr_t)actor); break; } uint64_t tsc2 = ponyint_cpu_tick(); if(quiescent(sched, tsc, tsc2)) { DTRACE2(WORK_STEAL_FAILURE, (uintptr_t)sched, (uintptr_t)victim); return NULL; } // If we have been passed an actor (implicitly, the cycle detector), and // enough time has elapsed without stealing or quiescing, return the actor // we were passed (allowing the cycle detector to run). if((prev != NULL) && ((tsc2 - tsc) > 10000000000)) { actor = prev; break; } } send_msg(0, SCHED_UNBLOCK, 0); return actor; }
/** * Use mpmcqs to allow stealing directly from a victim, without waiting for a * response. */ static pony_actor_t* steal(scheduler_t* sched, pony_actor_t* prev) { send_msg(0, SCHED_BLOCK, 0); uint64_t tsc = ponyint_cpu_tick(); pony_actor_t* actor; while(true) { scheduler_t* victim = choose_victim(sched); if(victim == NULL) victim = sched; actor = pop_global(victim); if(actor != NULL) break; uint64_t tsc2 = ponyint_cpu_tick(); if(quiescent(sched, tsc, tsc2)) return NULL; // If we have been passed an actor (implicitly, the cycle detector), and // enough time has elapsed without stealing or quiescing, return the actor // we were passed (allowing the cycle detector to run). if((prev != NULL) && ((tsc2 - tsc) > 10000000000)) { actor = prev; break; } } send_msg(0, SCHED_UNBLOCK, 0); return actor; }
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; }
gint threadSearch(Position* position, gint alpha, gint beta, gint depth, gint hight, const gboolean nullMove, const gboolean nodePV){ Moves moves; Move move, bestMove = Empty, hMove = Empty, pMove=Empty; gboolean extend=FALSE, endGamePhase; gint eval, tmpDepth, movesSearched = 0, pAlpha, pType; guint idleThreads; guint64 key = position->key, hData; HashData* hd; if(stopFlag == TRUE && !ponderSearch) return EVAL_DRAW; searchNodes++; /* Draw */ if(positionRepeat(position) >= 1 || position->staticMoves >= 100) return EVAL_DRAW; /* End game */ gint numberOfPieces = numberOfBits64(position->allPieces); if(numberOfPieces <= 5 && egbbIsLoaded()){ tmpDepth = (2 * iterativeDepth) / 3; if((numberOfPieces <= 4 || hight <= tmpDepth) && (hight >= tmpDepth || positionEvalEG(position))){ if(egbbProbe(position, &eval)){ if(eval < 0) return eval + hight * 40 + positionMaterial(position); if(eval > 0) return eval - hight * 40 + positionMaterial(position); return eval; } } } /* Mate distance pruning */ eval = -EVAL_MATE + hight; if(eval > alpha){ if(eval >= beta) return eval; alpha = eval; } eval = EVAL_MATE - hight; if(eval < beta){ if(eval <= alpha) return eval; beta = eval; } /* Hash */ if(hashFind(&hd,key,&hData)){ hMove = hashMove(hData); if(!nodePV && (gint)hashDepth(hData) >= depth){ if(hashFlag(hData) == EXACT) return hashEval(hData); else if(hashFlag(hData) == ALPHA && hashEval(hData) > alpha){ bestMove = hashMove(hData); alpha = hashEval(hData); if(alpha >= beta) return alpha; } else if(hashFlag(hData) == BETA && hashEval(hData) < beta){ beta = hashEval(hData); if(alpha >= beta) return beta; } } } /* Extension */ if(position->extend || (nodePV && passedPawnMove(position))){ depth ++; extend = TRUE; } /* Horizon */ if(depth < 1){ eval = quiescent(position,alpha,beta,hight+1,TRUE); return eval; } if(position->phase <= 7 && numberOfPieces <= 10) endGamePhase = TRUE; else endGamePhase = FALSE; /* Null move pruning */ if (!nodePV && nullMove && depth>2 && !extend && !endGamePhase){ if(depth > 6) tmpDepth = depth - 4; else tmpDepth = depth - 3; moveDo(position,Null_Move); eval = -threadSearch(position,-beta,-beta+1,tmpDepth,hight+1,FALSE,FALSE); moveUndo(position); if(eval >= beta){ hashWrite(hd,key,depth,eval,ALPHA,Empty); return eval; } } /* Internal Iterative Deepening */ tmpDepth = depth/2; if(nodePV && hMove == Empty && depth > 3 && tmpDepth >= (gint)hashDepth(hData)){ threadSearch(position,alpha, beta, tmpDepth, hight, FALSE,nodePV); if(hashFind2(hd,key,&hData)) hMove = hashMove(hData); } /* First move */ moves.state = START; if(hMove != Empty){ move = hMove; moveDo(position,move); } else move = nextMoveDo(position,&moves); if(move != Empty){ eval = -threadSearch(position, -beta, -alpha, depth-1, hight+1, TRUE,nodePV); if(eval > alpha){ if(eval >= beta){ moveUndo(position); if(moves.state == STATIC) history[moveGetFrom(move)][moveGetTo(move)] += 1<<depth; hashWrite(hd,key,depth,eval,ALPHA,move); return eval; } alpha = eval; bestMove = move; } moveUndo(position); movesSearched ++; } /* Parallel search*/ idleThreads = searchThreads; while(TRUE){ if(idleThreads > 0){ move = nextMoveDo(position,&moves); /* Last moves searched sequential */ if(move == Empty){ parallelStopFlag = TRUE; moves.size = moves.next = 0; while(idleThreads != searchThreads){ parallelSearchResult(&pMove,&pAlpha,&pType); idleThreads++; moves.move[moves.size] = pMove; moves.size ++; } parallelStopFlag = FALSE; moves.toSort = moves.size; break; } /* History pruning */ if(!nodePV && movesSearched >= HistoryFullDepthMoves && depth > HistoryReductionLimit && !extend && !position->extend){ idleThreads--; parallelSearch(position, move, -(alpha+1), -alpha, depth-2, hight+1,TRUE,HP,FALSE); moveUndo(position); movesSearched ++; } else{ /* PVS */ idleThreads--; parallelSearch(position, move, -(alpha+1), -alpha, depth-1, hight+1,TRUE,PVS,FALSE); moveUndo(position); movesSearched ++; } } else{ eval = -parallelSearchResult(&pMove,&pAlpha,&pType); idleThreads++; pAlpha = -pAlpha - 1; if(pType == HP && eval > pAlpha){ /* HP */ idleThreads--; moveDo(position,pMove); parallelSearch(position, pMove, -(pAlpha+1), -pAlpha, depth-1, hight+1,TRUE,PVS,FALSE); moveUndo(position); continue; } if(pType == PVS && eval > pAlpha && eval < beta){ /* PVS research */ idleThreads--; moveDo(position,pMove); parallelSearch(position, pMove, -beta, -pAlpha, depth-1, hight+1,TRUE,RPVS,nodePV); moveUndo(position); continue; } if(eval > alpha){ if(eval >= beta){ parallelSearchStop(idleThreads); if(moveGetType(pMove) == Normal) history[moveGetFrom(pMove)][moveGetTo(pMove)] += 1<<depth; hashWrite(hd,key,depth,eval,ALPHA,pMove); return eval; } alpha = eval; bestMove = pMove; } } } /* Last moves searched sequential */ move = nextMoveDo(position,&moves); while(move != Empty){ /* History pruning */ if(!nodePV && movesSearched >= HistoryFullDepthMoves && depth > HistoryReductionLimit && !extend && !position->extend){ eval = -threadSearch(position, -(alpha+1), -alpha, depth-2, hight+1, TRUE, FALSE); } else eval = alpha + 1; /* PVS */ if(eval > alpha){ eval = -threadSearch(position, -(alpha+1), -alpha, depth-1, hight+1, TRUE, FALSE); if(eval > alpha && eval < beta) eval = -threadSearch(position, -beta, -alpha, depth-1, hight+1, TRUE, nodePV); } moveUndo(position); if(eval > alpha){ if(eval >= beta){ if(moveGetType(pMove) == Normal) history[moveGetFrom(move)][moveGetTo(move)] += 1<<depth; hashWrite(hd,key,depth,eval,ALPHA,move); return eval; } alpha = eval; bestMove = move; } movesSearched ++; move = nextMoveDo(position,&moves); } /* Mate or pat */ if(movesSearched == 0){ if(position->inCheck == TRUE){ eval = -EVAL_MATE+hight; hashWrite(hd,key,depth,eval,EXACT,Empty); return eval; } else{ hashWrite(hd,key,depth,EVAL_DRAW,EXACT,Empty); return EVAL_DRAW; } } /* hash is update */ if(bestMove != Empty) hashWrite(hd,key,depth,alpha,EXACT,bestMove); else hashWrite(hd,key,depth,alpha,BETA,hMove); return alpha; }