/// <summary> /// Executes the parsing. /// </summary> public static void Run() { Restrictions.Output = OutputType.UCI; Engine engine = new Engine(); Position position = Position.Create(Position.StartingFEN); String command; while ((command = Console.ReadLine()) != null) { List <String> terms = new List <String>(command.Split(' ')); switch (terms[0]) { default: Terminal.WriteLine("Unknown command: {0}", terms[0]); Terminal.WriteLine("Enter \"help\" for assistance."); break; case "uci": Terminal.WriteLine("id name " + engine.Name); Terminal.WriteLine("id author Zong Zheng Li"); Terminal.WriteLine("option name Hash type spin default " + Engine.DefaultHashAllocation + " min 1 max 2047"); Terminal.WriteLine("uciok"); break; case "ucinewgame": engine.Reset(); break; case "setoption": if (terms.Contains("Hash")) { engine.HashAllocation = Int32.Parse(terms[terms.IndexOf("value") + 1]); } break; case "position": String fen = Position.StartingFEN; if (terms[1] != "startpos") { fen = command.Substring(command.IndexOf("fen") + 4); } position = Position.Create(fen); Int32 movesIndex = terms.IndexOf("moves"); if (movesIndex >= 0) { for (Int32 i = movesIndex + 1; i < terms.Count; i++) { position.Make(Move.Create(position, terms[i])); } } break; case "go": Restrictions.Reset(); for (Int32 i = 1; i < terms.Count; i++) { switch (terms[i]) { default: case "infinite": break; case "depth": Restrictions.Depth = Int32.Parse(terms[i + 1]); Restrictions.UseTimeControls = false; break; case "movetime": Restrictions.MoveTime = Int32.Parse(terms[i + 1]); Restrictions.UseTimeControls = false; break; case "wtime": Restrictions.TimeControl[Colour.White] = Int32.Parse(terms[i + 1]); Restrictions.UseTimeControls = true; break; case "btime": Restrictions.TimeControl[Colour.Black] = Int32.Parse(terms[i + 1]); Restrictions.UseTimeControls = true; break; case "winc": Restrictions.TimeIncrement[Colour.White] = Int32.Parse(terms[i + 1]); Restrictions.UseTimeControls = true; break; case "binc": Restrictions.TimeIncrement[Colour.Black] = Int32.Parse(terms[i + 1]); Restrictions.UseTimeControls = true; break; case "nodes": Restrictions.Nodes = Int32.Parse(terms[i + 1]); Restrictions.UseTimeControls = false; break; case "ponder": // TODO: implement command. break; case "mate": // TODO: implement command. break; case "movestogo": // TODO: implement command. break; } } new Thread(new ThreadStart(() => { Int32 bestMove = engine.GetMove(position); Terminal.WriteLine("bestmove " + Stringify.Move(bestMove)); })) { IsBackground = true }.Start(); break; case "stop": engine.Stop(); break; case "isready": Terminal.WriteLine("readyok"); break; case "quit": return; case "perft": Perft.Iterate(position, Int32.Parse(terms[1])); break; case "divide": Perft.Divide(position, Int32.Parse(terms[1])); break; case "draw": Terminal.WriteLine(position); break; case "fen": Terminal.WriteLine(position.GetFEN()); break; case "ponderhit": // TODO: implement command. break; case "register": // TODO: implement command. break; case "help": Terminal.WriteLine("Command Function"); Terminal.WriteLine("-----------------------------------------------------------------------"); Terminal.WriteLine("position [fen] Sets the current position to the position denoted"); Terminal.WriteLine(" by the given FEN. \"startpos\" is accepted for the"); Terminal.WriteLine(" starting position"); Terminal.WriteLine("go [type] [number] Searches the current position. Search types include"); Terminal.WriteLine(" \"movetime\", \"depth\", \"nodes\", \"wtime\", \"btime\","); Terminal.WriteLine(" \"winc\", and \"binc\""); Terminal.WriteLine("perft [number] Runs perft() on the current position to the given"); Terminal.WriteLine(" depth"); Terminal.WriteLine("divide [number] Runs divide() on the current position for the given"); Terminal.WriteLine(" depth"); Terminal.WriteLine("fen Prints the FEN of the current position."); Terminal.WriteLine("draw Draws the current position"); Terminal.WriteLine("stop Stops an ongoing search"); Terminal.WriteLine("quit Exits the application"); Terminal.WriteLine("-----------------------------------------------------------------------"); break; } } }
public static void Main(String[] args) { // Display terminal window by default. We will hide it later if necessary. Terminal.Initialize(); // Run as GUI application if there are no command-line arguments. Display // the Settings window, which will display the main board window as needed. if (args.Length == 0) { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new Settings()); } // Run as command-line application if there are command-line arguments. else { Action run = null; List <String> epd = new List <String>(); for (Int32 i = 0; i < args.Length; i++) { if (args[i].EndsWith(".epd", StringComparison.InvariantCultureIgnoreCase)) { using (StreamReader sr = new StreamReader(args[i])) while (!sr.EndOfStream) { String line = sr.ReadLine(); if (line.Length > 0) { epd.Add(line); } } } else { switch (args[i]) { // Unrecognized command. default: Terminal.WriteLine("Unrecognized parameter: {0}", args[i]); Terminal.WriteLine("Valid parameters are:"); Terminal.WriteLine("-u UCI/command-line mode"); Terminal.WriteLine("-t Tournament mode"); Terminal.WriteLine("-s Test suite mode"); Terminal.WriteLine("-m [number] Limit move time"); Terminal.WriteLine("-d [number] Limit depth"); Terminal.WriteLine("-n [number] Limit nodes"); break; // Limit move time. case "-m": Restrictions.MoveTime = Int32.Parse(args[++i]); break; // Limit depth. case "-d": Restrictions.Depth = Int32.Parse(args[++i]); break; // Limit nodes. case "-n": Restrictions.Nodes = Int32.Parse(args[++i]); break; // UCI mode. case "uci": case "-uci": case "-u": run = () => { UCI.Run(); }; break; // Tournament mode. case "-t": run = () => { Tournament.Run(epd); }; break; // Test suite mode. case "-s": run = () => { TestSuite.Run(epd); }; break; } } } run(); } }
/// <summary> /// Returns the best move for the given position as determined by an /// iterative deepening search framework. This is the main entry point for /// the search algorithm. /// </summary> /// <param name="position">The position to search on.</param> /// <returns>The predicted best move.</returns> private Int32 Search(Position position) { // Generate legal moves. Return immediately if there is only one legal move // when playing with time controls. List <Int32> moves = position.LegalMoves(); if (Restrictions.UseTimeControls && moves.Count <= 1) { return(moves[0]); } // Initialize variables to prepare for search. Int32 colour = position.SideToMove; Int32 depthLimit = Math.Min(DepthLimit, Restrictions.Depth); _timeLimit = Restrictions.MoveTime; _timeExtension = 0; // Allocate search time when playing with time controls. if (Restrictions.UseTimeControls) { Double timeAllocation = Restrictions.TimeControl[colour] / Math.Max(20, Math.Ceiling(60 * Math.Exp(-0.007 * position.HalfMoves))); _timeLimit = timeAllocation + Restrictions.TimeIncrement[colour] - TimeControlsExpectedLatency; _timeExtensionLimit = 0.3 * Restrictions.TimeControl[colour] - timeAllocation; } // Apply iterative deepening. The search is repeated with incrementally // higher depths until it is terminated. for (Int32 depth = 1; depth <= depthLimit; depth++) { Int32 alpha = -Infinity; // Go through the move list. for (Int32 i = 0; i < moves.Count; i++) { _movesSearched++; Int32 value = alpha + 1; Int32 move = moves[i]; Boolean causesCheck = position.CausesCheck(move); position.Make(move); // Apply principal variation search with aspiration windows. The first move // is searched with a window centered around the best value found from the // most recent preceding search. If the result does not lie within the // window, a re-search is initiated with an open window. if (i == 0) { Int32 lower = _rootAlpha - AspirationWindow; Int32 upper = _rootAlpha + AspirationWindow; value = -Search(position, depth - 1, 1, -upper, -lower, causesCheck); if (value <= lower || value >= upper) { TryTimeExtension(TimeControlsResearchThreshold, TimeControlsResearchExtension); value = -Search(position, depth - 1, 1, -Infinity, Infinity, causesCheck); } } // Subsequent moves are searched with a zero window search. If the result is // better than the best value so far, a re-search is initiated with a wider // window. else { value = -Search(position, depth - 1, 1, -alpha - 1, -alpha, causesCheck); if (value > alpha) { value = -Search(position, depth - 1, 1, -Infinity, -alpha, causesCheck); } } // Unmake the move and check for search termination. position.Unmake(move); if (_abortSearch) { goto exit; } // Check for new best move. If the current move has the best value so far, // it is moved to the front of the list. This ensures the best move is // always the first move in the list, also gives a rough ordering of the // moves, and so subsequent searches are more efficient. The principal // variation is collected at this point. if (value > alpha) { alpha = _rootAlpha = value; moves.RemoveAt(i); moves.Insert(0, move); PrependPV(move, 0); _pv = GetPrincipalVariation(); // Output principal variation for high depths. This happens on every depth // increase and every time an improvement is found. if (Restrictions.Output != OutputType.None && depth > SingleVariationDepth) { Terminal.WriteLine(GetPVString(position, depth, alpha, _pv)); } } } // Output principal variation for low depths. This happens once for every // depth since improvements are very frequent. if (Restrictions.Output != OutputType.None && depth <= SingleVariationDepth) { Terminal.WriteLine(GetPVString(position, depth, alpha, _pv)); } // Check for early search termination. If there is no time extension and a // significiant proportion of time has already been used, so that completing // one more depth is unlikely, the search is terminated. if (Restrictions.UseTimeControls && _timeExtension <= 0 && _stopwatch.ElapsedMilliseconds / _timeLimit > TimeControlsContinuationThreshold) { goto exit; } } exit: _finalAlpha = _rootAlpha; return(moves[0]); }