public static val qsearch(cPosicion pos, cStackMov[] ss, int ssPos, val alpha, val beta, ply depth, NodeType NT, bool InCheck) { bool PvNode = (NT == cTipoNodo.PV); cPosInfo st = null; cTablaHashStruct tte; hash posKey; mov ttMove, move, bestMove; val bestValue, value, ttValue, futilityValue, futilityBase, oldAlpha = 0; bool givesCheck, evasionPrunable; ply ttDepth; if (PvNode) oldAlpha = alpha; ss[ssPos].currentMove = bestMove = cMovType.MOV_NAN; ss[ssPos].ply = ss[ssPos - 1].ply + 1; if (pos.IsTablas() || ss[ssPos].ply > cSearch.MAX_PLY) return ss[ssPos].ply > cSearch.MAX_PLY && !InCheck ? cEval.Eval(pos) : DrawValue[pos.ColorMueve()]; ttDepth = ((InCheck || depth >= cPly.DEPTH_QS_CHECKS) ? cPly.DEPTH_QS_CHECKS : cPly.DEPTH_QS_NO_CHECKS); posKey = pos.ClaveHash(); tte = cMotor.m_TablaHash.Buscar(posKey); ttMove = (tte != null ? tte.GetMove() : cMovType.MOV_NAN); ttValue = tte != null ? value_from_tt(tte.GetValue(), ss[ssPos].ply) : cValoresJuego.NAN; if (tte != null && tte.GetDepth() >= ttDepth && ttValue != cValoresJuego.NAN && (PvNode ? tte.GetBound() == cBordes.BOUND_EXACT : (ttValue >= beta) ? (tte.GetBound() & cBordes.BOUND_LOWER) != 0 : (tte.GetBound() & cBordes.BOUND_UPPER) != 0)) { ss[ssPos].currentMove = ttMove; return ttValue; } if (InCheck) { ss[ssPos].staticEval = cValoresJuego.NAN; bestValue = futilityBase = -cValoresJuego.INFINITO; } else { if (tte != null) { if ((ss[ssPos].staticEval = bestValue = tte.GetValueEval()) == cValoresJuego.NAN) ss[ssPos].staticEval = bestValue = cEval.Eval(pos); if (ttValue != cValoresJuego.NAN) if ((tte.GetBound() & (ttValue > bestValue ? cBordes.BOUND_LOWER : cBordes.BOUND_UPPER)) != 0) bestValue = ttValue; } else ss[ssPos].staticEval = bestValue = cEval.Eval(pos); if (bestValue >= beta) { if (tte == null) cMotor.m_TablaHash.Save(pos.ClaveHash(), value_to_tt(bestValue, ss[ssPos].ply), cBordes.BOUND_LOWER, cPly.DEPTH_NONE, cMovType.MOV_NAN, ss[ssPos].staticEval); return bestValue; } if (PvNode && bestValue > alpha) alpha = bestValue; futilityBase = bestValue + 128; } cMovOrder mp = new cMovOrder(pos, ttMove, depth, History, cTypes.GetToCasilla(ss[ssPos - 1].currentMove)); cInfoJaque ci = new cInfoJaque(pos); st = new cPosInfo(); while ((move = mp.SiguienteMovimientoEnFalso()) != cMovType.MOV_NAN) { givesCheck = cTypes.TipoMovimiento(move) == cMovType.NORMAL && 0 == ci.m_Candidatas ? (ci.m_Jaque[cTypes.TipoPieza(pos.GetPieza(cTypes.GetFromCasilla(move)))] & cBitBoard.m_nCasillas[cTypes.GetToCasilla(move)]) != 0 : pos.IsJaque(move, ci); if (!PvNode && !InCheck && !givesCheck && move != ttMove && futilityBase > -cValoresJuego.GANA && !pos.IsPeonAvanzado(move)) { futilityValue = futilityBase + cPosicion.m_nValPieza[cFaseJuego.FASE_FINAL][pos.GetPieza(cTypes.GetToCasilla(move))]; if (futilityValue < beta) { bestValue = Math.Max(bestValue, futilityValue); continue; } if (futilityBase < beta && pos.SEE(move) <= cValoresJuego.CERO) { bestValue = Math.Max(bestValue, futilityBase); continue; } } evasionPrunable = InCheck && bestValue > cValoresJuego.MATE_MAXIMO_VS && !pos.IsCaptura(move) && 0 == pos.CanEnroque(pos.ColorMueve()); if (!PvNode && (!InCheck || evasionPrunable) && move != ttMove && cTypes.TipoMovimiento(move) != cMovType.PROMOCION && pos.SEEReducido(move) < cValoresJuego.CERO) continue; if (!pos.IsLegalMov(move, ci.m_Clavadas)) continue; ss[ssPos].currentMove = move; pos.DoMov(move, st, ci, givesCheck); value = givesCheck ? -qsearch(pos, ss, ssPos + 1, -beta, -alpha, depth - cPly.ONE_PLY, NT, true) : -qsearch(pos, ss, ssPos + 1, -beta, -alpha, depth - cPly.ONE_PLY, NT, false); pos.DesMov(move); if (value > bestValue) { bestValue = value; if (value > alpha) { if (PvNode && value < beta) { alpha = value; bestMove = move; } else { cMotor.m_TablaHash.Save(posKey, value_to_tt(value, ss[ssPos].ply), cBordes.BOUND_LOWER, ttDepth, move, ss[ssPos].staticEval); return value; } } } } if (InCheck && bestValue == -cValoresJuego.INFINITO) return cTypes.MateEnVs(ss[ssPos].ply); cMotor.m_TablaHash.Save(posKey, value_to_tt(bestValue, ss[ssPos].ply), PvNode && bestValue > oldAlpha ? cBordes.BOUND_EXACT : cBordes.BOUND_UPPER, ttDepth, bestMove, ss[ssPos].staticEval); return bestValue; }