private int AssessTimeForMiniMaxDepth(int depth, IList <SingleMove> availableMoves, Board board, int previousDepth, DiagnosticsData previousData) { var previousTime = previousData.TimeElapsed.TotalMilliseconds; var prevousEvalCount = previousData.EvaluationCount + previousData.CheckCount; // Need a equation to model evalcount <-> time // movecount ^ depth = evalcount // evalcount correlates to time // Estimated evaluation speed. moves per millisecond // Speed = count / time var previousEvalSpeed = prevousEvalCount / previousTime; var evalCount = Math.Pow(Math.Max(availableMoves.Count, 6), depth); // lets say 20 ^ 5 = 3 200 000 // Estimated total time with previous speed // Time = count / speed var timeEstimate = evalCount / previousEvalSpeed; // Increase estimate proportionally depending of piece count // All pieces -> use as is // 1 piece -> 1/16 of the time var powerPieces = board.PieceList.Count(p => Math.Abs(p.RelativeStrength) > PieceBaseStrength.Pawn); var factor = (double)powerPieces / 16; //var factor = 0.5 + (double)powerPieces / 32; return((int)(timeEstimate * factor)); }
public int DecideSearchDepth(DiagnosticsData previous, List <SingleMove> allMoves, Board board) { _previous = previous; var previousDepth = SearchDepth; // Previous was opening move from database if (previous.EvaluationCount == 0 && previous.CheckCount == 0) { Diagnostics.AddMessage($"Using search depth {SearchDepth}."); return(SearchDepth); } // Logic needs to be rewritten when using transposition tables, because of how much they affect speed. if (_useTranspositionTables) { SearchDepth = GetMaxDepthForCurrentBoardWithTranspositions(board); Diagnostics.AddMessage($"Using search depth {SearchDepth}."); return(SearchDepth); } var maxDepth = GetMaxDepthForCurrentBoard(board); SearchDepth = maxDepth; // TODO Skip until correlated to new speeds Diagnostics.AddMessage($"Using search depth {SearchDepth}."); return(maxDepth); var previousEstimate = 0; for (int i = 3; i <= maxDepth; i++) { var estimation = AssessTimeForMiniMaxDepth(i, allMoves, board, previousDepth, previous); // Use 50% tolerance for target time if (estimation > TargetTime * 1.50) { SearchDepth = i - 1; Diagnostics.AddMessage($"Using search depth {SearchDepth}. Time estimation was {previousEstimate} ms."); return(SearchDepth); } else { previousEstimate = estimation; } } SearchDepth = maxDepth; Diagnostics.AddMessage($"Failed to assess - using search depth {SearchDepth}. "); return(maxDepth); //AnalyzeGamePhase(allMoves.Count, board); //return SearchDepth; }
public (int searchDepth, GamePhase gamePhase) DecideSearchDepth(DiagnosticsData previous, List <SingleMove> allMoves, Board board) { // Testing if (previous.OverrideSearchDepth != null) { return(previous.OverrideSearchDepth.Value, previous.OverrideGamePhase); } _previous = previous; AnalyzeGamePhase(allMoves.Count, board); return(SearchDepth, Phase); }
/// <summary> /// Call in start of each player turn /// </summary> public static void StartMoveCalculations() { _currentData = new DiagnosticsData(); _timeElapsed.Start(); }
/// <summary> /// Refresh data in beginning of each turn /// </summary> /// <param name="data"></param> /// <param name="turnCount"></param> public void Update(DiagnosticsData data, int turnCount) { _previous = data; TurnCount = turnCount; }
public override IPlayerMove CreateMove() { if (_connectionTestOverride) { var diagnostics = Diagnostics.CollectAndClear(); // Dummy moves for connection testing var move = new PlayerMoveImplementation() { Move = new MoveImplementation() { StartPosition = $"a{_connectionTestIndex--}", EndPosition = $"a{_connectionTestIndex}", PromotionResult = PromotionPieceType.NoPromotion }, Diagnostics = diagnostics.ToString() }; return(move); } else { var isMaximizing = IsPlayerWhite; Diagnostics.StartMoveCalculations(); // Get all available moves and do necessary filtering List <SingleMove> allMoves = Board.Moves(isMaximizing, true, true).ToList(); if (allMoves.Count == 0) { throw new ArgumentException($"No possible moves for player [isWhite={IsPlayerWhite}]. Game should have ended to draw (stalemate)."); } // Reorder moves to improve alpha-beta cutoffs // allMoves = MoveResearch.OrderMoves(allMoves, Board, isMaximizing); if (MoveHistory.IsLeaningToDraw(GameHistory)) { var repetionMove = GameHistory[GameHistory.Count - 4]; allMoves.RemoveAll(m => m.PrevPos.ToAlgebraic() == repetionMove.StartPosition && m.NewPos.ToAlgebraic() == repetionMove.EndPosition); } Diagnostics.AddMessage($"Available moves found: {allMoves.Count}. "); Strategy.Update(PreviousData, TurnCount); var strategyResult = Strategy.DecideSearchDepth(PreviousData, allMoves, Board); SearchDepth = strategyResult.searchDepth; Phase = strategyResult.gamePhase; var bestMove = AnalyzeBestMove(allMoves); if (bestMove == null) { throw new ArgumentException($"Board didn't contain any possible move for player [isWhite={IsPlayerWhite}]."); } // Update local Board.ExecuteMove(bestMove); TurnCount++; // Endgame checks // TODO should be now read from singlemove var castling = false; var check = Board.IsCheck(IsPlayerWhite); //var checkMate = false; //if(check) checkMate = Board.IsCheckMate(IsPlayerWhite, true); if (bestMove.Promotion) { Diagnostics.AddMessage($"Promotion occured at {bestMove.NewPos.ToAlgebraic()}. "); } PreviousData = Diagnostics.CollectAndClear(); var move = new PlayerMoveImplementation() { Move = bestMove.ToInterfaceMove(castling, check), Diagnostics = PreviousData.ToString() }; GameHistory.Add(move.Move); return(move); } }