static void DoMove(struct resRes *menuData, MENUITEM *item, MENUITEM *dest) { if (item != dest) { MENUITEM **itemplace = GetItemPlace(&menuData->resource->u.menu->items, item); MENUITEM **destplace; if (dest) { destplace = GetItemPlace(&menuData->resource->u.menu->items, dest); } else { destplace = itemplace; while (*destplace) destplace = &(*destplace)->next; } if (itemplace && destplace) { UndoMove(menuData, itemplace, item); *itemplace = (*itemplace)->next; item->next = *destplace; *destplace = item; ResSetDirty(menuData); InvalidateRect(menuData->activeHwnd, 0, FALSE); } } }
void DfpnSolver::LookupChildDataNonConst(SgMove move, DfpnData& data) { PlayMove(move); if (! TTRead(data)) { data.m_bounds.phi = 1; data.m_bounds.delta = 1; data.m_work = 0; } UndoMove(); }
void UndoGameMove(GAME *Game) { if (Game->MoveNo == 0) { Error("Game->MoveNo == 0 in UndoGameMove()"); return; } Game->MoveNo--; UndoMove(Game->Board,Game->Move[Game->MoveNo].Move); if (RestoreClocks) ElapsedTime[Game->Board->Colour>0] -= Game->Move[Game->MoveNo].Time; TurnBegin = CurrentTime(); }
void DfpnSolver::GetPVFromHash(PointSequence& pv) { // to do: SgAssertRestore r(state of search in subclass); int nuMoves = 0; for (;; ++nuMoves) { DfpnData data; if ( ! TTRead(data) || data.m_bestMove == SG_NULLMOVE ) break; pv.push_back(data.m_bestMove); PlayMove(data.m_bestMove); } while (--nuMoves >= 0) UndoMove(); }
void GameTree::PrintGame(FILE* f){ int i; char pre[100]; for(i=0;i<currDepth;i++) pre[i]=' '; sprintf(pre+i, "%3ld %2d", currIdx, currDepth); if(currIdx==0){ fprintf(f,"Size=%zu\n",size); fprintf(f,"%s %d %d %0.1f\n",pre,MoveValue(),val,IsWin()); }else{ fprintf(f,"%s %d %d %0.1f\n",pre,MoveValue(),val,IsWin()); } if(currDepth<depth) for(int i=0;i<breath;i++){ DoMove(i); PrintGame(f); UndoMove(); } }
bool DfpnSolver::Validate(DfpnHashTable& hashTable, const SgBlackWhite winner, SgSearchTracer& tracer) { SG_ASSERT_BW(winner); DfpnData data; if (! TTRead(data)) { PointSequence pv; StartSearch(hashTable, pv); const bool wasRead = TTRead(data); SG_DEBUG_ONLY(wasRead); SG_ASSERT(wasRead); } const bool orNode = (winner == GetColorToMove()); if (orNode) { if (! data.m_bounds.IsWinning()) { SgWarning() << "OR not winning. DfpnData:" << data << std::endl; return false; } } else // AND node { if (! data.m_bounds.IsLosing()) { SgWarning() << "AND not losing. DfpnData:" << data << std::endl; return false; } } SgEmptyBlackWhite currentWinner; if (TerminalState(GetColorToMove(), currentWinner)) { if (winner == currentWinner) return true; else { SgWarning() << "winner disagreement: " << SgEBW(winner) << ' ' << SgEBW(currentWinner) << std::endl; return false; } } std::vector<SgMove> moves; if (orNode) moves.push_back(data.m_bestMove); else // AND node GenerateChildren(moves); // recurse for (std::vector<SgMove>::const_iterator it = moves.begin(); it != moves.end(); ++it) { tracer.AddTraceNode(*it, GetColorToMove()); PlayMove(*it); if (! Validate(hashTable, winner, tracer)) return false; UndoMove(); tracer.TakeBackTraceNode(); } return true; }
size_t DfpnSolver::MID(const DfpnBounds& maxBounds, DfpnHistory& history) { maxBounds.CheckConsistency(); SG_ASSERT(maxBounds.phi > 1); SG_ASSERT(maxBounds.delta > 1); ++m_numMIDcalls; size_t prevWork = 0; SgEmptyBlackWhite colorToMove = GetColorToMove(); DfpnData data; if (TTRead(data)) { prevWork = data.m_work; if (! maxBounds.GreaterThan(data.m_bounds)) // Estimated bounds are larger than we had // anticipated. The calling state must have computed // the max bounds with out of date information, so just // return here without doing anything: the caller will // now update to this new info and carry on. return 0; } else { SgEmptyBlackWhite winner = SG_EMPTY; if (TerminalState(colorToMove, winner)) { ++m_numTerminal; DfpnBounds terminal; if (colorToMove == winner) DfpnBounds::SetToWinning(terminal); else { SG_ASSERT(SgOppBW(colorToMove) == winner); DfpnBounds::SetToLosing(terminal); } TTWrite(DfpnData(terminal, SG_NULLMOVE, 1)); return 1; } } ++m_generateMoves; DfpnChildren children; GenerateChildren(children.Children()); // Not thread safe: perhaps move into while loop below later... std::vector<DfpnData> childrenData(children.Size()); for (size_t i = 0; i < children.Size(); ++i) LookupData(childrenData[i], children, i); // Index used for progressive widening size_t maxChildIndex = ComputeMaxChildIndex(childrenData); SgHashCode currentHash = Hash(); SgMove bestMove = SG_NULLMOVE; DfpnBounds currentBounds; size_t localWork = 1; do { UpdateBounds(currentBounds, childrenData, maxChildIndex); if (! maxBounds.GreaterThan(currentBounds)) break; // Select most proving child std::size_t bestIndex = 999999; DfpnBoundType delta2 = DfpnBounds::INFTY; SelectChild(bestIndex, delta2, childrenData, maxChildIndex); bestMove = children.MoveAt(bestIndex); // Compute maximum bound for child const DfpnBounds childBounds(childrenData[bestIndex].m_bounds); DfpnBounds childMaxBounds; childMaxBounds.phi = maxBounds.delta - (currentBounds.delta - childBounds.phi); childMaxBounds.delta = delta2 == DfpnBounds::INFTY ? maxBounds.phi : std::min(maxBounds.phi, std::max(delta2 + 1, DfpnBoundType(delta2 * (1.0 + m_epsilon)))); SG_ASSERT(childMaxBounds.GreaterThan(childBounds)); if (delta2 != DfpnBounds::INFTY) m_deltaIncrease.Add(float(childMaxBounds.delta-childBounds.delta)); // Recurse on best child PlayMove(bestMove); history.Push(bestMove, currentHash); localWork += MID(childMaxBounds, history); history.Pop(); UndoMove(); // Update bounds for best child LookupData(childrenData[bestIndex], children, bestIndex); // Compute some stats when find winning move if (childrenData[bestIndex].m_bounds.IsLosing()) { m_moveOrderingIndex.Add(float(bestIndex)); m_moveOrderingPercent.Add(float(bestIndex) / (float)childrenData.size()); m_totalWastedWork += prevWork + localWork - childrenData[bestIndex].m_work; } else if (childrenData[bestIndex].m_bounds.IsWinning()) maxChildIndex = ComputeMaxChildIndex(childrenData); } while (! CheckAbort()); // Find the most delaying move for losing states, and the smallest // winning move for winning states. if (currentBounds.IsSolved()) { if (currentBounds.IsLosing()) { std::size_t maxWork = 0; for (std::size_t i = 0; i < children.Size(); ++i) { if (childrenData[i].m_work > maxWork) { maxWork = childrenData[i].m_work; bestMove = children.MoveAt(i); } } } else { std::size_t minWork = DfpnBounds::INFTY; for (std::size_t i = 0; i < children.Size(); ++i) { if (childrenData[i].m_bounds.IsLosing() && childrenData[i].m_work < minWork) { minWork = childrenData[i].m_work; bestMove = children.MoveAt(i); } } } } // Store search results TTWrite(DfpnData(currentBounds, bestMove, localWork + prevWork)); return localWork; }
static int negascout(struct SearchData *sd, int alpha, int beta, const int depth, int node_type #if MP ,int exclusiveP #endif /* MP */ ) { struct Position *p = sd->position; struct SearchStatus *st; int best = -INF; int bestm = M_NONE; int tmp; int talpha; int incheck; int lmove; int move; int extend = 0; int threat = FALSE; int reduce_extensions; int next_type; int was_futile = FALSE; #if FUTILITY int is_futile; int optimistic = 0; #endif #if MP int deferred_cnt = 0; int deferred_list[MAX_DEFERRED]; int deferred_depth[MAX_DEFERRED]; #endif EnterNode(sd); Nodes++; /* check for search termination */ if (sd->master && TerminateSearch(sd)) { AbortSearch = TRUE; goto EXIT; } /* max search depth reached */ if (sd->ply >= MaxDepth) goto EXIT; /* * Check for insufficent material or theoretical draw. */ if ( /* InsufMat(p) || CheckDraw(p) || */ Repeated(p, FALSE)) { best = 0; goto EXIT; } /* * check extension */ incheck = InCheck(p, p->turn); if (incheck && p->material[p->turn] > 0) { extend += CheckExtend(p); ChkExt++; } /* * Check the hashtable */ st = sd->current; HTry++; #if MP switch (ProbeHT(p->hkey, &tmp, depth, &(st->st_hashmove), &threat, sd->ply, exclusiveP, sd->localHashTable)) #else switch (ProbeHT(p->hkey, &tmp, depth, &(st->st_hashmove), &threat, sd->ply)) #endif /* MP */ { case ExactScore: HHit++; best = tmp; goto EXIT; case UpperBound: if (tmp <= alpha) { HHit++; best = tmp; goto EXIT; } break; case LowerBound: if (tmp >= beta) { HHit++; best = tmp; goto EXIT; } break; case Useless: threat = !incheck && MateThreat(p, OPP(p->turn)); break; #if MP case OnEvaluation: best = -ON_EVALUATION; goto EXIT; #endif } /* * Probe EGTB */ if (depth > EGTBDepth && ProbeEGTB(p, &tmp, sd->ply)) { best = tmp; goto EXIT; } /* * Probe recognizers */ switch (ProbeRecognizer(p, &tmp)) { case ExactScore: best = tmp; goto EXIT; case LowerBound: if (tmp >= beta) { best = tmp; goto EXIT; } break; case UpperBound: if (tmp <= alpha) { best = tmp; goto EXIT; } break; } #if NULLMOVE /* * Null move search. * See Christian Donninger, "Null Move and Deep Search" * ICCA Journal Volume 16, No. 3, pp. 137-143 */ if (!incheck && node_type == CutNode && !threat) { int next_depth; int nms; next_depth = depth - ReduceNullMove; if (next_depth > 0) { next_depth = depth - ReduceNullMoveDeep; } DoNull(p); if (next_depth < 0) { nms = -quies(sd, -beta, -beta+1, 0); } else { #if MP nms = -negascout(sd, -beta, -beta+1, next_depth, AllNode, 0); #else nms = -negascout(sd, -beta, -beta+1, next_depth, AllNode); #endif } UndoNull(p); if (AbortSearch) goto EXIT; if (nms >= beta) { if (p->nonPawn[p->turn] >= Value[Queen]) { best = nms; goto EXIT; } else { if (next_depth < 0) { nms = quies(sd, beta-1, beta, 0); } else { #if MP nms = negascout(sd, beta-1, beta, next_depth, CutNodeNoNull, 0); #else nms = negascout(sd, beta-1, beta, next_depth, CutNodeNoNull); #endif } if (nms >= beta) { best = nms; goto EXIT; } else { extend += ExtendZugzwang; ZZExt++; } } } else if (nms <= -CMLIMIT) { threat = TRUE; } } #endif /* NULLMOVE */ lmove = (p->actLog-1)->gl_Move; reduce_extensions = (sd->ply > 2*sd->depth); talpha = alpha; switch (node_type) { case AllNode: next_type = CutNode; break; case CutNode: case CutNodeNoNull: next_type = AllNode; break; default: next_type = PVNode; break; } #if FUTILITY is_futile = !incheck && !threat && alpha < CMLIMIT && alpha > -CMLIMIT; if (is_futile) { if (p->turn == White) { optimistic = MaterialBalance(p) + MaxPos; } else { optimistic = -MaterialBalance(p) + MaxPos; } } #endif /* FUTILITY */ /* * Internal iterative deepening. If we do not have a move, we try * a shallow search to find a good candidate. */ if (depth > 2*OnePly && ((alpha + 1) != beta) && !LegalMove(p, st->st_hashmove)) { int useless; #if MP useless = negascout(sd, alpha, beta, depth-2*OnePly, PVNode, 0); #else useless = negascout(sd, alpha, beta, depth-2*OnePly, PVNode); #endif st->st_hashmove = sd->pv_save[sd->ply+1]; } /* * Search all legal moves */ while ((move = incheck ? NextEvasion(sd) : NextMove(sd)) != M_NONE) { int next_depth = extend; if (move & M_CANY && !MayCastle(p, move)) continue; /* * recapture extension */ if ((move & M_CAPTURE) && (lmove & M_CAPTURE) && M_TO(move) == M_TO(lmove) && IsRecapture(p->piece[M_TO(move)], (p->actLog-1)->gl_Piece)) { RCExt += 1; next_depth += ExtendRecapture[TYPE(p->piece[M_TO(move)])]; } /* * passed pawn push extension */ if (TYPE(p->piece[M_FROM(move)]) == Pawn && p->nonPawn[OPP(p->turn)] <= Value[Queen]) { int to = M_TO(move); if (((p->turn == White && to >= a7) || (p->turn == Black && to <= h2)) && IsPassed(p, to, p->turn) && SwapOff(p, move) >= 0) { next_depth += ExtendPassedPawn; PPExt += 1; } } /* * limit extensions to sensible range. */ if (reduce_extensions) next_depth /= 2; next_depth += depth - OnePly; #if FUTILITY /* * Futility cutoffs */ if (is_futile) { if (next_depth < 0 && !IsCheckingMove(p, move)) { tmp = optimistic + ScoreMove(p, move); if (tmp <= alpha) { if (tmp > best) { best = tmp; bestm = move; was_futile = TRUE; } continue; } } #if EXTENDED_FUTILITY /* * Extended futility cutoffs and limited razoring. * See Ernst A. Heinz, "Extended Futility Pruning" * ICCA Journal Volume 21, No. 2, pp 75-83 */ else if (next_depth >= 0 && next_depth < OnePly && !IsCheckingMove(p, move)) { tmp = optimistic + ScoreMove(p, move) + (3*Value[Pawn]); if (tmp <= alpha) { if (tmp > best) { best = tmp; bestm = move; was_futile = TRUE; } continue; } } #if RAZORING else if (next_depth >= OnePly && next_depth < 2*OnePly && !IsCheckingMove(p, move)) { tmp = optimistic + ScoreMove(p, move) + (6*Value[Pawn]); if (tmp <= alpha) { next_depth -= OnePly; } } #endif /* RAZORING */ #endif /* EXTENDED_FUTILITY */ } #endif /* FUTILITY */ DoMove(p, move); if (InCheck(p, OPP(p->turn))) { UndoMove(p, move); } else { /* * Check extension */ if (p->material[p->turn] > 0 && InCheck(p, p->turn)) { next_depth += (reduce_extensions) ? ExtendInCheck>>1 : ExtendInCheck; } /* * Recursively search this position. If depth is exhausted, use * quies, otherwise use negascout. */ if (next_depth < 0) { tmp = -quies(sd, -beta, -talpha, 0); } else if (bestm != M_NONE && !was_futile) { #if MP tmp = -negascout(sd, -talpha-1, -talpha, next_depth, next_type, bestm != M_NONE); if (tmp != ON_EVALUATION && tmp > talpha && tmp < beta) { tmp = -negascout(sd, -beta, -tmp, next_depth, node_type == PVNode ? PVNode : AllNode, bestm != M_NONE); } #else tmp = -negascout(sd, -talpha-1, -talpha, next_depth, next_type); if (tmp > talpha && tmp < beta) { tmp = -negascout(sd, -beta, -tmp, next_depth, node_type == PVNode ? PVNode : AllNode); } #endif /* MP */ } else { #if MP tmp = -negascout(sd, -beta, -talpha, next_depth, next_type, bestm != M_NONE); #else tmp = -negascout(sd, -beta, -talpha, next_depth, next_type); #endif /* MP */ } UndoMove(p, move); if (AbortSearch) goto EXIT; #if MP if (tmp == ON_EVALUATION) { /* * This child is ON_EVALUATION. Remember move and * depth. */ deferred_list[deferred_cnt] = move; deferred_depth[deferred_cnt] = next_depth; deferred_cnt++; } else { #endif /* MP */ /* * beta cutoff, enter move in Killer/Countermove table */ if (tmp >= beta) { if (!(move & M_TACTICAL)) { PutKiller(sd, move); sd->counterTab[p->turn][lmove & 4095] = move; } StoreResult(sd, tmp, alpha, beta, move, depth, threat); best = tmp; goto EXIT; } /* * Improvement on best move to date */ if (tmp > best) { best = tmp; bestm = move; was_futile = FALSE; if (best > talpha) { talpha = best; } } next_type = CutNode; #if MP } #endif /* MP */ } }
static int quies(struct SearchData *sd, int alpha, int beta, int depth) { struct Position *p = sd->position; int best; int move; int talpha; int tmp; QNodes++; EnterNode(sd); /* max search depth reached */ if (sd->ply >= MaxDepth || Repeated(p, FALSE)) { best = 0; goto EXIT; } /* * Probe recognizers. If the probe is successful, use the * recognizer score as evaluation score. * * Otherwise, use ScorePosition() */ switch (ProbeRecognizer(p, &tmp)) { case ExactScore: best = tmp; goto EXIT; case LowerBound: best = tmp; if (best >= beta) { goto EXIT; } break; case UpperBound: best = tmp; if (best <= alpha) { goto EXIT; } break; default: best = ScorePosition(p, alpha, beta); break; } if (best >= beta) { goto EXIT; } talpha = MAX(alpha, best); while ((move = NextMoveQ(sd, alpha) ) != M_NONE) { DoMove(p, move); if (InCheck(p, OPP(p->turn))) UndoMove(p, move); else { tmp = -quies(sd, -beta, -talpha, depth-1); UndoMove(p, move); if (tmp >= beta) { best = tmp; goto EXIT; } if (tmp > best) { best = tmp; if (best > talpha) { talpha = best; } } } } EXIT: LeaveNode(sd); return best; }
static void Undo(char *args) { if(CurrentPosition->ply > 0) { UndoMove(CurrentPosition, (CurrentPosition->actLog-1)->gl_Move); } }