/// <summary> /// Instruct the computer to think and make its next move. /// </summary> public void Think() { // Determine the best move available for "this" player instance, from the current board position. Debug.WriteLine( string.Format( "Thread {0} is " + (this.IsPondering ? "pondering" : "thinking"), Thread.CurrentThread.Name)); Player player = this.MyPlayer; // Set the player, whose move is to be computed, to "this" player object instance this.PrincipalVariation = new Moves(); // Best moves line (Principal Variation) found so far. // TimeSpan tsnTimePondered = new TimeSpan(); int intTurnNo = Game.TurnNo; // Set whether to build a post-analysis tree of positions searched try { if (!this.IsPondering && !Game.IsInAnalyseMode) { // Query Simple Opening Book if (Game.UseRandomOpeningMoves) { Move moveBook; if ((moveBook = OpeningBookSimple.SuggestRandomMove(player)) != null) { this.PrincipalVariation.Add(moveBook); this.MoveConsideredEvent(); throw new ForceImmediateMoveException(); } } /* Query Best Opening Book * if ((m_moveBest = OpeningBook.SearchForGoodMove(Board.HashCodeA, Board.HashCodeB, this.Colour) )!=null) * { * m_moveCurrent = m_moveBest; * this.MoveConsidered(); * throw new ForceImmediateMoveException(); * } */ } // Time allowed for this player to think if (Game.ClockFixedTimePerMove.TotalSeconds > 0) { // Absolute fixed time per move. No time is carried over from one move to the next. this.ThinkingTimeAllotted = Game.ClockFixedTimePerMove; } else if (Game.ClockIncrementPerMove.TotalSeconds > 0) { // Incremental clock this.ThinkingTimeAllotted = new TimeSpan( Game.ClockIncrementPerMove.Ticks + (( ((Game.ClockIncrementPerMove.Ticks * Game.MoveNo) + (Game.ClockTime.Ticks * Math.Min(Game.MoveNo, 40) / 40)) - this.MyPlayer.Clock.TimeElapsed.Ticks) / 3)); // Make sure we never think for less than half the "Increment" time this.ThinkingTimeAllotted = new TimeSpan( Math.Max(this.ThinkingTimeAllotted.Ticks, (Game.ClockIncrementPerMove.Ticks / 2) + 1)); } else if (Game.ClockMaxMoves == 0 && Game.ClockIncrementPerMove.TotalSeconds <= 0) { // Fixed game time this.ThinkingTimeAllotted = new TimeSpan(this.MyPlayer.Clock.TimeRemaining.Ticks / 30); } else { // Conventional n moves in x minutes time this.ThinkingTimeAllotted = new TimeSpan(this.MyPlayer.Clock.TimeRemaining.Ticks / this.MyPlayer.Clock.MovesRemaining); } // Minimum of 1 second thinking time if (this.ThinkingTimeAllotted.TotalSeconds < 1) { this.ThinkingTimeAllotted = new TimeSpan(0, 0, 1); } // The computer only stops "thinking" when it has finished a full ply of thought, // UNLESS m_tsnThinkingTimeMaxAllowed is exceeded, or clock runs out, then it stops right away. if (Game.ClockFixedTimePerMove.TotalSeconds > 0) { // Fixed time per move this.ThinkingTimeMaxAllowed = Game.ClockFixedTimePerMove; } else { // Variable time per move this.ThinkingTimeMaxAllowed = new TimeSpan( Math.Min( this.ThinkingTimeAllotted.Ticks * 2, this.MyPlayer.Clock.TimeRemaining.Ticks - (new TimeSpan(0, 0, 0, 2)).Ticks)); } // Minimum of 2 seconds thinking time if (this.ThinkingTimeMaxAllowed.TotalSeconds < 2) { this.ThinkingTimeMaxAllowed = new TimeSpan(0, 0, 2); } // Total number of times the evaluation function has been called (May be less than PositonsSearched if hashtable works well) if (Game.IsInAnalyseMode) { HashTable.Clear(); HashTableCheck.Clear(); HashTablePawnKing.Clear(); History.Clear(); } else { if (this.MyPlayer.CanClaimMoveRepetitionDraw(2)) { // See if' we're in a 2 move repetition position, and if so, clear the hashtable, as old hashtable entries corrupt 3MR detection HashTable.Clear(); } else { HashTable.ResetStats(); // Reset the main hash table hit stats } HashTableCheck.ResetStats(); // We also have a hash table in which we just store the check status for both players HashTablePawnKing.ResetStats(); // And finally a hash table that stores the positional score of just the pawns. History.Clear(); // Clear down the History Heuristic info, at the start of each move. } if (!this.IsPondering) { this.MyPlayer.Clock.Start(); } int score = this.Search.IterativeDeepeningSearch( this.MyPlayer, this.PrincipalVariation, this.ThinkingTimeAllotted, this.ThinkingTimeMaxAllowed); } catch (ForceImmediateMoveException x) { // Undo any moves made during thinking Debug.WriteLine(x.ToString()); while (Game.TurnNo > intTurnNo) { Move.Undo(Game.MoveHistory.Last); } } if (this.MoveConsideredEvent != null) { this.MoveConsideredEvent(); } Debug.WriteLine( string.Format( "Thread {0} is ending " + (this.IsPondering ? "pondering" : "thinking"), Thread.CurrentThread.Name)); this.threadThought = null; if (this.MoveConsideredEvent != null && !this.IsPondering) { this.ReadyToMakeMoveEvent(); } this.IsPondering = false; // Send total elapsed time to generate this move. WinBoard.SendMoveTime(DateTime.Now - this.MyPlayer.Clock.TurnStartTime); }