internal Info(string response)
        {
            this._originalResponse = response;
            var tokens = UCIResponse.GetTokens(response.ToLower());

            for (int i = 0; i < tokens.Count; i++)
            {
                switch (tokens[i])
                {
                case "depth":
                    i++;
                    Depth = int.Parse(tokens[i]);
                    break;

                case "seldepth":
                    i++;
                    SelDepth = int.Parse(tokens[i]);
                    break;

                case "multipv":
                    i++;
                    MultiPv = int.Parse(tokens[i]);
                    break;

                case "score":
                    i++;
                    switch (tokens[i])
                    {
                    case "cp":
                        i++;
                        CpScore = int.Parse(tokens[i]);
                        if (tokens[i + 1] == "lowerbound")
                        {
                            IsLowerBounndScore = true;
                        }
                        if (tokens[i + 1] == "upperbound")
                        {
                            IsUpperBoundScore = true;
                        }
                        break;

                    case "mate":
                        i++;
                        MateScore = int.Parse(tokens[i]);
                        break;
                    }
                    break;

                case "nodes":
                    i++;
                    Nodes = int.Parse(tokens[i]);
                    break;

                case "nps":
                    i++;
                    Nps = int.Parse(tokens[i]);
                    break;

                case "tbhits":
                    i++;
                    TbHits = int.Parse(tokens[i]);
                    break;

                case "time":
                    i++;
                    Time = int.Parse(tokens[i]);
                    break;

                case "pv":
                    i++;
                    var moves = new List <string>();
                    for (int j = i; j < tokens.Count; j++)
                    {
                        moves.Add(tokens[j]);
                    }
                    Pv = new ReadOnlyCollection <string>(moves);
                    break;

                case "currmove":
                    i++;
                    CurrMove = tokens[i];
                    break;

                case "currmovenumber":
                    i++;
                    CurrMoveNumber = int.Parse(tokens[i]);
                    break;
                }
            }
        }
        internal Option(string response)
        {
            this._originalResponse = response;
            response = response.Substring("option name ".Length);
            var typeIndex = response.IndexOf(" type ", StringComparison.InvariantCultureIgnoreCase);

            if (typeIndex == -1)
            {
                throw new ArgumentException("Unable to find the «type» keyword in this option.");
            }
            this.Name = response.Substring(0, typeIndex);
            response  = response.Substring(typeIndex + " type ".Length);
            if (response.StartsWith("string default", StringComparison.InvariantCultureIgnoreCase))
            {
                //  option name Debug Log File type string default
                //  option name EvalFile type string default nn-82215d0fd0df.nnue
                this.OptionType = optionType.@string;
                var tokens = UCIResponse.GetTokens(response);
                if (tokens.Count == 3)
                {
                    this.StringDefaultValue = tokens[2];
                }
            }
            else if (response.StartsWith("spin default ", StringComparison.InvariantCultureIgnoreCase))
            {
                //  option name Contempt type spin default 24 min -100 max 100
                this.OptionType = optionType.spin;
                var tokens = UCIResponse.GetTokens(response);
                this.SpinDefaultValue = int.Parse(tokens[2]);
                this.SpinMinValue     = int.Parse(tokens[4]);
                this.SpinMaxValue     = int.Parse(tokens[6]);
            }
            else if (response.StartsWith("combo default ", StringComparison.InvariantCultureIgnoreCase))
            {
                //  option name Analysis Contempt type combo default Both var Off var White var Black var Both
                this.OptionType = optionType.combo;
                var tokens = UCIResponse.GetTokens(response);
                this.ComboDefaultValue = tokens[2];
                var comboValues = new List <string>();
                for (int i = 4; i < tokens.Count; i += 2)
                {
                    comboValues.Add(tokens[i]);
                }
                this.ComboValues = new ReadOnlyCollection <string>(comboValues);
            }
            else if (response.StartsWith("button", StringComparison.InvariantCultureIgnoreCase))
            {
                //  option name Clear Hash type button
                this.OptionType = optionType.button;
            }
            else if (response.StartsWith("check default ", StringComparison.InvariantCultureIgnoreCase))
            {
                //  option name Ponder type check default false
                this.OptionType = optionType.check;
                var tokens = UCIResponse.GetTokens(response);
                this.CheckDefaultValue = bool.Parse(tokens[2]);
            }
            else
            {
                throw new ArgumentException("Unable to identify the type of this option.");
            }
        }