private void BestMove(int maxDepth) { if (null == _gameBoard) { throw new NoBoardException(); } if (_gameBoard.GameIsOver) { throw new GameIsOverException(); } StopPonder(); CancellationToken token = OnStartAsyncCommand(); Task <Move> task = _gameAI.GetBestMoveAsync(_gameBoard.Clone(), maxDepth, Config.MaxHelperThreads, token); task.Wait(); if (null == task.Result) { throw new Exception("Null move returned!"); } ConsoleOut(NotationUtils.ToBoardSpaceMoveString(_gameBoard, task.Result)); OnEndAsyncCommand(); }
private void BestMove(TimeSpan maxTime) { if (null == _gameBoard) { throw new NoBoardException(); } if (_gameBoard.GameIsOver) { throw new GameIsOverException(); } StopPonder(); CancellationToken token = OnStartAsyncCommand(); if (maxTime < TimeSpan.MaxValue) { _asyncCommandCTS.CancelAfter(maxTime); } Task <Move> task = _gameAI.GetBestMoveAsync(_gameBoard, maxTime, Config.MaxHelperThreads, token); task.Wait(); if (null == task.Result) { throw new Exception("Null move returned!"); } ConsoleOut(NotationUtils.ToBoardSpaceMoveString(_gameBoard, task.Result)); OnEndAsyncCommand(); }
private void Play(string moveString) { if (null == _gameBoard) { throw new NoBoardException(); } if (_gameBoard.GameIsOver) { throw new GameIsOverException(); } Move move = null; try { move = NotationUtils.ParseMoveString(_gameBoard, moveString); } catch (Exception) { throw new InvalidMoveException(move); } NotationUtils.TryNormalizeBoardSpaceMoveString(moveString, out moveString); _gameBoard.Play(move, moveString); StopPonder(); ConsoleOut(_gameBoard.ToGameString()); }
private void OnBestMoveFound(object sender, BestMoveFoundEventArgs args) { if (null != _gameBoard && !_isPondering && Config.ReportIntermediateBestMoves) { ConsoleOut("{0};{1};{2:0.00}", NotationUtils.ToBoardSpaceMoveString(_gameBoard, args.Move), args.Depth, args.Score); } }
double evaluateFingerObstacleCost(FingerState state, float minPreparation, NoteChord obsNote, float startPosition, int startHeight, int pitch) { float deltaX = startPosition - Piano.KeyPositions[pitch]; float deltaY = startHeight - Piano.getKeyHeight(pitch); double distance = Math.Min(Math.Sqrt(deltaX * deltaX + deltaY * deltaY), 5) + 1; float prepareTime = startTime - minPreparation; float importance = NotationUtils.getNoteImportanceInChord(obsNote, state.Pitch); debug += string.Format("O{0},{1},{2},{3};", state.Pitch, prepareTime, state.Release, state.Press); if (prepareTime >= state.Release) { double coeff = CostCoeff.FINGER_MOVE_SPEED_PUNISH * Math.Pow(0.1, (startTime - state.Release) / minPreparation); return(coeff * distance); } else if (prepareTime >= state.Press) { double speedCost = CostCoeff.FINGER_MOVE_SPEED_PUNISH * distance; double cutoffCost = CostCoeff.NOTE_CUTOFF_PUNISH * importance * (state.Release - prepareTime) / (state.Release - state.Press); return(speedCost + cutoffCost); } else { return(CostCoeff.OMIT_KEY_PUNISH * importance); } }
private void ProcessBestMove(string line, bool tryToPlay) { try { Move bestMove = NotationUtils.ParseMoveString(Board, line.Split(';')[0]); TargetPiece = bestMove.PieceName; TargetPosition = bestMove.Position; TargetMove = bestMove; } catch (Exception) { TargetPiece = PieceName.INVALID; } if (tryToPlay && CurrentTurnIsEngineAI && CurrentGameSettings.GameMode == GameMode.Play && null != TargetMove) { if (TargetMove.IsPass) { SendCommandInternal("pass"); } else { SendCommandInternal("play {0}", NotationUtils.ToBoardSpaceMoveString(Board, TargetMove)); } } }
public void SavePGN(Stream outputStream) { if (null == outputStream) { throw new ArgumentNullException(nameof(outputStream)); } using (StreamWriter sw = new StreamWriter(outputStream, Encoding.ASCII)) { // Write Mandatory Tags sw.WriteLine(GetPGNTag("GameType", EnumUtils.GetExpansionPiecesString(Metadata.GameType))); sw.WriteLine(GetPGNTag("Date", Metadata.Date)); sw.WriteLine(GetPGNTag("Event", Metadata.Event)); sw.WriteLine(GetPGNTag("Site", Metadata.Site)); sw.WriteLine(GetPGNTag("Round", Metadata.Round)); sw.WriteLine(GetPGNTag("White", Metadata.White)); sw.WriteLine(GetPGNTag("Black", Metadata.Black)); sw.WriteLine(GetPGNTag("Result", Metadata.Result.ToString())); // Write Optional Tags foreach (KeyValuePair <string, string> tag in Metadata.OptionalTags) { sw.WriteLine(GetPGNTag(tag.Key, tag.Value)); } if (GameBoard.BoardHistory.Count > 0) { sw.WriteLine(); WritePGNMoveCommentary(sw, 0); // Write Moves int count = 1; foreach (BoardHistoryItem item in GameBoard.BoardHistory) { sw.WriteLine("{0}. {1}", count, NotationUtils.NormalizeBoardSpaceMoveString(item.MoveString)); WritePGNMoveCommentary(sw, count); count++; } } // Write Result if (EnumUtils.GameIsOver(Metadata.Result)) { sw.WriteLine(); sw.WriteLine(Metadata.Result.ToString()); } } }
public void Parse(string s) { if (string.IsNullOrWhiteSpace(s)) { throw new ArgumentNullException(nameof(s)); } s = s.Trim(); string[] vals = s.Split('\t'); Board = GameBoard.ParseGameString(vals[0]); InvalidMoveString = vals[1]; InvalidMove = NotationUtils.ParseMoveString(Board, vals[1]); }
private void ValidMoves() { if (null == _gameBoard) { throw new NoBoardException(); } if (_gameBoard.GameIsOver) { throw new GameIsOverException(); } MoveSet validMoves = _gameBoard.GetValidMoves(); ConsoleOut(NotationUtils.ToBoardSpaceMoveStringList(_gameBoard, validMoves)); }
public void Parse(string s) { if (string.IsNullOrWhiteSpace(s)) { throw new ArgumentNullException(nameof(s)); } s = s.Trim(); string[] vals = s.Split('\t'); Board = GameBoard.ParseGameString(vals[0]); ValidMoveStrings = vals[1].Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries); ValidMoves = new Move[ValidMoveStrings.Length]; for (int i = 0; i < ValidMoveStrings.Length; i++) { ValidMoves[i] = NotationUtils.ParseMoveString(Board, ValidMoveStrings[i]); } }
public void PlayTargetMove() { if (CurrentGameSettings.GameMode != GameMode.Play) { throw new Exception("Please switch the current game to play mode first."); } if (null == TargetMove) { throw new Exception("Please select a valid piece and destination first."); } if (TargetMove.IsPass) { Pass(); } else { SendCommand("play {0}", NotationUtils.ToBoardSpaceMoveString(Board, TargetMove)); } }
public void load() { if (!SourceAsset) { Debug.LogError("SourceAsset is null."); return; } MemoryStream stream = new MemoryStream(); stream.Write(SourceAsset.bytes, 0, SourceAsset.bytes.Length); stream.Position = 0; MidiSeq = new Midi.Sequence(); MidiSeq.Load(stream); if (TrackHandIndices == null || TrackHandIndices.Length != MidiSeq.Count) { TrackHandIndices = Enumerable.Repeat(-1, MidiSeq.Count).ToArray(); } Notation = NotationUtils.parseMidiFile(MidiSeq); }
public void run() { if (MidiSeq.Count == 0) { Debug.LogWarning("MIDI no track found."); return; } if (!HandConfigLib) { HandConfigLib = GetComponent <HandConfigLibrary>(); } if (!HandConfigLib) { Debug.LogError("HandConfigLibrary is null."); } var trackMap = HandTracksMap; Fingering[] results = new Fingering[trackMap.Count]; int resultIndex = 0; foreach (var pair in trackMap) { if (pair.Key >= Hands.Length) { Debug.LogErrorFormat("Hand index {0} out of hand config range.", pair.Key); return; } Hand hand = Hands[pair.Key]; int[] trackList = pair.Value; NotationTrack[] tracks = new NotationTrack[trackList.Length]; for (int track = 0; track < trackList.Length; ++track) { tracks[track] = Notation[trackList[track]]; } Navigator.Track = NotationTrack.merge(tracks); Navigator.Config = HandConfigLib.getConfig(hand.Config); Navigator.KeepConstraints = KeepConstraints; Navigator.MinStepCount = StepCountMin; Navigator.MaxStepCount = StepCountMax; Navigator.BubbleLength = BubbleLength; Navigator.EstimationStepIncrement = EstimationStepIncrement; if (Navigator.Config == null) { Debug.LogErrorFormat("Hand config of {0} is null.", hand.Config); return; } Navigator.HandType = hand.Type; Navigator.AdaptionSpeed = hand.AdaptionSpeed; results[resultIndex++] = Navigator.run(); if (DumpTree) { #if UNITY_EDITOR UnityEditor.EditorUtility.DisplayProgressBar("FingeringGenerator", "DumpTree ...", 0); #endif FileStream file = new FileStream(Application.dataPath + "/Editor/Log/FingeringNavigatorTreeDump" + pair.Key.ToString() + ".txt", FileMode.Create); byte[] bytes = System.Text.Encoding.Default.GetBytes(Navigator.getTreeJsonDump()); #if UNITY_EDITOR UnityEditor.EditorUtility.DisplayProgressBar("FingeringGenerator", string.Format("DumpTree {0:n} bytes...", bytes.Length), 0); #endif file.Write(bytes, 0, bytes.Length); file.Close(); #if UNITY_EDITOR UnityEditor.EditorUtility.ClearProgressBar(); #endif } /*// dump leaf nodes * { * FileStream file = new FileStream(Application.dataPath + "/Editor/Log/leaves.txt", FileMode.Create); * * file.WriteByte((byte)'['); * List<FingeringNavigator.TreeNode> leaves = Navigator.TreeLeaves; * leaves.Sort(delegate(FingeringNavigator.TreeNode node1, FingeringNavigator.TreeNode node2) * { * double cost1 = node1.CommittedCost; * double cost2 = node2.CommittedCost; * * return cost1.CompareTo(cost2); * }); * foreach(var leaf in leaves) * { * byte[] bytes = System.Text.Encoding.Default.GetBytes(leaf.JsonDump); * file.Write(bytes, 0, bytes.Length); * file.WriteByte((byte)','); * } * file.WriteByte((byte)'{'); * file.WriteByte((byte)'}'); * file.WriteByte((byte)']'); * * file.Close(); * }*/ } NotationUtils.appendFingeringToMidiFile(MidiSeq, results); }
public static GameRecording LoadSGF(Stream inputStream, string fileName = null) { if (null == inputStream) { throw new ArgumentNullException(nameof(inputStream)); } GameMetadata metadata = new GameMetadata(); List <string> moveList = new List <string>(); Dictionary <string, Stack <string> > backupPositions = new Dictionary <string, Stack <string> >(); string rawResult = ""; bool lastMoveCompleted = true; bool whiteTurn = true; using (StreamReader sr = new StreamReader(inputStream)) { string line = null; while ((line = sr.ReadLine()) != null) { line = line.Trim(); if (!string.IsNullOrWhiteSpace(line)) { // Line has contents Match m = null; if ((m = Regex.Match(line, @"SU\[(.*)\]")).Success) { metadata.SetTag("GameType", m.Groups[1].Value.ToUpper().Replace("HIVE", EnumUtils.NoExpansionsString).Replace("-", "+")); } else if ((m = Regex.Match(line, @"EV\[(.*)\]")).Success) { metadata.SetTag("Event", m.Groups[1].Value); } else if ((m = Regex.Match(line, @"PC\[(.*)\]")).Success) { metadata.SetTag("Site", m.Groups[1].Value); } else if ((m = Regex.Match(line, @"RO\[(.*)\]")).Success) { metadata.SetTag("Round", m.Groups[1].Value); } else if ((m = Regex.Match(line, @"DT\[(.+)\]")).Success) { string rawDate = m.Groups[1].Value; metadata.SetTag("SgfDate", rawDate); rawDate = Regex.Replace(rawDate, @"(\D{3}) (\D{3}) (\d{2}) (\d{2}):(\d{2}):(\d{2}) (.{3,}) (\d{4})", @"$1 $2 $3 $4:$5:$6 $8"); if (DateTime.TryParseExact(rawDate, SgfDateFormats, CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime parsed)) { metadata.SetTag("Date", parsed.ToString("yyyy.MM.dd")); } else { foreach (CultureInfo ci in CultureInfo.GetCultures(CultureTypes.AllCultures)) { if (DateTime.TryParseExact(rawDate, SgfDateFormats, ci, DateTimeStyles.None, out parsed)) { metadata.SetTag("Date", parsed.ToString("yyyy.MM.dd")); break; } } } } else if ((m = Regex.Match(line, @"P0\[id ""(.*)""\]")).Success) { metadata.SetTag("White", m.Groups[1].Value); } else if ((m = Regex.Match(line, @"P1\[id ""(.*)""\]")).Success) { metadata.SetTag("Black", m.Groups[1].Value); } else if ((m = Regex.Match(line, @"RE\[(.+)\]")).Success) { rawResult = m.Groups[1].Value; metadata.SetTag("SgfResult", m.Groups[1].Value); } else if ((m = Regex.Match(line, @"GN\[(.+)\]")).Success) { metadata.SetTag("SgfGameName", m.Groups[1].Value); } else if ((m = Regex.Match(line, @"((move (w|b))|(dropb)|(pdropb)) ([a-z0-9]+) ([a-z] [0-9]+) ([a-z0-9\\\-\/\.]*)", RegexOptions.IgnoreCase)).Success) { // Initial parse string movingPiece = m.Groups[m.Groups.Count - 3].Value.ToLower(); string destination = m.Groups[m.Groups.Count - 1].Value.ToLower().Replace("\\\\", "\\"); string backupPos = m.Groups[m.Groups.Count - 2].Value; // Remove unnecessary numbers movingPiece = movingPiece.Replace("m1", "m").Replace("l1", "l").Replace("p1", "p"); destination = destination.Replace("m1", "m").Replace("l1", "l").Replace("p1", "p"); // Add missing color indicator if (movingPiece == "b1" || movingPiece == "b2" || !(movingPiece.StartsWith("b") || movingPiece.StartsWith("w"))) { movingPiece = (whiteTurn ? "w" : "b") + movingPiece; } // Fix missing destination if (destination == ".") { if (moveList.Count == 0) { destination = ""; } else { destination = backupPositions[backupPos].Peek(); } } // Remove move that wasn't commited if (!lastMoveCompleted) { moveList.RemoveAt(moveList.Count - 1); } moveList.Add(string.Format("{0} {1}", movingPiece, destination)); foreach (Stack <string> stack in backupPositions.Values) { if (stack.Count > 0 && stack.Peek() == movingPiece) { stack.Pop(); break; } } if (!backupPositions.ContainsKey(backupPos)) { backupPositions.Add(backupPos, new Stack <string>()); } backupPositions[backupPos].Push(movingPiece); lastMoveCompleted = false; } else if ((m = Regex.Match(line, @"P(0|1)\[[0-9]+ pass\]")).Success) { moveList.Add(NotationUtils.BoardSpacePass); lastMoveCompleted = false; } else if ((m = Regex.Match(line, @"P(0|1)\[[0-9]+ done\]")).Success) { lastMoveCompleted = true; whiteTurn = !whiteTurn; } else if ((m = Regex.Match(line, @"P(0|1)\[[0-9]+ resign\]")).Success) { rawResult = m.Groups[1].Value == "0" ? BoardState.BlackWins.ToString() : BoardState.WhiteWins.ToString(); } } } } GameBoard gameBoard = new GameBoard(metadata.GameType); foreach (string moveString in moveList) { Move move = null; try { move = NotationUtils.ParseMoveString(gameBoard, moveString); } catch (Exception ex) { throw new Exception(string.Format("Unable to parse '{0}'.", moveString), ex); } gameBoard.TrustedPlay(move, NotationUtils.NormalizeBoardSpaceMoveString(moveString)); } // Set result if (rawResult.Contains(metadata.White)) { metadata.SetTag("Result", BoardState.WhiteWins.ToString()); } else if (rawResult.Contains(metadata.Black)) { metadata.SetTag("Result", BoardState.BlackWins.ToString()); } else if (rawResult == "The game is a draw") { metadata.SetTag("Result", BoardState.Draw.ToString()); } else if (Enum.TryParse(rawResult, out BoardState parsed)) { metadata.SetTag("Result", parsed.ToString()); } else { metadata.SetTag("Result", gameBoard.BoardState.ToString()); } return(new GameRecording(gameBoard, GameRecordingSource.SGF, metadata) { FileName = fileName?.Trim() }); }
public static GameRecording LoadPGN(Stream inputStream, string fileName = null) { if (null == inputStream) { throw new ArgumentNullException(nameof(inputStream)); } GameMetadata metadata = new GameMetadata(); List <string> moveList = new List <string>(); string rawResult = ""; string multiLineCommentary = null; using (StreamReader sr = new StreamReader(inputStream, Encoding.ASCII)) { string line = null; while ((line = sr.ReadLine()) != null) { line = line.Trim(); if (null != multiLineCommentary) { // Line is part of multiline commentary multiLineCommentary += Environment.NewLine + line; if (multiLineCommentary.EndsWith("}")) { // End of multiline commentary metadata.SetMoveCommentary(moveList.Count, multiLineCommentary); multiLineCommentary = null; } } else if (line.StartsWith("[") && line.EndsWith("]")) { // Line is a tag KeyValuePair <string, string> kvp = ParsePGNTag(line); if (kvp.Key == "Result") { rawResult = kvp.Value; } else { metadata.SetTag(kvp.Key, kvp.Value); } } else if (line.StartsWith("{") && line.EndsWith("}")) { // Line is a single line of commentary metadata.SetMoveCommentary(moveList.Count, line); } else if (line.StartsWith("{") && null == multiLineCommentary) { multiLineCommentary = line; } else if (!string.IsNullOrWhiteSpace(line)) { // Line is a move or result if (Enum.TryParse(line, out BoardState lineResult)) { rawResult = lineResult.ToString(); } else { // Not a result, add as moveString moveList.Add(line.TrimStart('.', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0')); } } } } GameBoard gameBoard = new GameBoard(metadata.GameType); foreach (string moveString in moveList) { Move move = null; try { move = NotationUtils.ParseMoveString(gameBoard, moveString); } catch (Exception ex) { throw new Exception(string.Format("Unable to parse '{0}'.", moveString), ex); } gameBoard.TrustedPlay(move, moveString); } // Set result if (Enum.TryParse(rawResult, out BoardState result)) { metadata.SetTag("Result", result.ToString()); } else { metadata.SetTag("Result", gameBoard.BoardState.ToString()); } return(new GameRecording(gameBoard, GameRecordingSource.PGN, metadata) { FileName = fileName?.Trim() }); }
public ObservableBoardHistory(BoardHistory boardHistory, BoardHistory activeBoardHistory = null, Action <int> moveNumberChangedCallback = null) { _boardHistory = boardHistory ?? throw new ArgumentNullException(nameof(boardHistory)); _activeBoardHistory = activeBoardHistory ?? boardHistory; _moveNumberChangedCallback = moveNumberChangedCallback; if (_activeBoardHistory.Count > _boardHistory.Count) { throw new ArgumentException("Active history has more moves than history."); } int countWidth = _boardHistory.Count.ToString().Length; for (int i = 0; i < _boardHistory.Count; i++) { BoardHistoryItem item = _boardHistory[i]; string countString = (i + 1).ToString().PadLeft(countWidth) + ". "; string moveString = AppVM.ViewerConfig.NotationType == NotationType.BoardSpace ? NotationUtils.NormalizeBoardSpaceMoveString(item.MoveString) : item.ToString(); bool isActive = i < _activeBoardHistory.Count; bool isLastMove = i + 1 == _activeBoardHistory.Count; Items.Add(new ObservableBoardHistoryItem(countString + moveString, isActive, isLastMove)); } }
Choice evaluateChordChoice(FingerChord chord, int index) { float deltaTime = index > 0 ? NoteSeq[index].start - NoteSeq[index - 1].start : 0; NoteChord note = NoteSeq[index]; HandConfig.RangePair wrists = Config.getFingerChordWristRange(chord); double cost = 0; // wrist position naturality reward if (wrists.left != null) { float distance = Math.Abs(wrists.left.middle - -HandConfig.WristNaturePosition); cost = Math.Pow(distance / 14, 4) * CostCoeff.WRIST_POSITION_NATURALITY_REWARD; } if (wrists.right != null) { float distance = Math.Abs(wrists.right.middle - HandConfig.WristNaturePosition); cost = Math.Pow(distance / 14, 4) * CostCoeff.WRIST_POSITION_NATURALITY_REWARD; } // wrist crowd punish if (wrists.left != null && wrists.right != null) { float distance = Math.Max(Math.Abs(wrists.left.high - wrists.right.low), Math.Abs(wrists.right.high - wrists.left.low)); if (distance < 5) { cost += CostCoeff.WRIST_CROWD_PUNISH * (5f - distance) / 5f; } } foreach (Finger f in chord.Values) { // shift fingers punish if (Math.Abs((int)f) > 10) { cost += CostCoeff.SHIFT_FINGERS_PUNISH; } } int leftFingerCount = 0; int rightFingerCount = 0; foreach (var pair in chord) { if (pair.Value != Finger.EMPTY) { // black key short punish if (Piano.isBlackKey(pair.Key)) { int finger = Math.Abs((int)pair.Value); int first = (int)Math.Floor(finger / 10f) - 1; int second = finger % 10 - 1; float sh = HandConfig.BlackKeyShort[second]; if (first >= 0) { sh = Math.Max(HandConfig.BlackKeyShort[first], sh); } cost += sh * CostCoeff.BLACK_KEY_SHORT_PUNISH; } if (pair.Value > Finger.EMPTY) { ++rightFingerCount; } else if (pair.Value < Finger.EMPTY) { ++leftFingerCount; } } else { // omit key punish float importance = NotationUtils.getNoteImportanceInChord(note, pair.Key); cost += CostCoeff.OMIT_KEY_PUNISH * importance; } } // multiple fingers punish if (leftFingerCount > 0) { float value = leftFingerCount / 5f; cost += CostCoeff.MULTIPLE_FINGERS_PUNISH * value * value; } if (rightFingerCount > 0) { float value = rightFingerCount / 5f; cost += CostCoeff.MULTIPLE_FINGERS_PUNISH * value * value; } return(new Choice { chord = chord, staticCost = cost, wrists = wrists, deltaTime = deltaTime, node = new TreeNodeChoice() }); }
private void ProcessEngineOutput(EngineCommand command, string[] outputLines) { string errorMessage = ""; string invalidMoveMessage = ""; for (int i = 0; i < outputLines.Length; i++) { if (outputLines[i].StartsWith("err")) { errorMessage += outputLines[i].Substring(outputLines[i].IndexOf(' ') + 1) + Environment.NewLine; } else if (outputLines[i].StartsWith("invalidmove")) { invalidMoveMessage += outputLines[i].Substring(outputLines[i].IndexOf(' ') + 1) + Environment.NewLine; } } if (!string.IsNullOrWhiteSpace(errorMessage)) { throw new EngineErrorException(errorMessage.Trim(), outputLines); } if (!string.IsNullOrWhiteSpace(invalidMoveMessage)) { throw new EngineInvalidMoveException(invalidMoveMessage.Trim(), outputLines); } string firstLine = ""; string lastLine = ""; if (null != outputLines && outputLines.Length > 0) { firstLine = outputLines[0]; lastLine = outputLines[outputLines.Length - 1]; } // Update other properties switch (command) { case EngineCommand.NewGame: case EngineCommand.Play: case EngineCommand.Pass: case EngineCommand.Undo: Board = !string.IsNullOrWhiteSpace(firstLine) ? GameBoard.ParseGameString(firstLine, true) : null; break; case EngineCommand.ValidMoves: ValidMoves = !string.IsNullOrWhiteSpace(firstLine) ? NotationUtils.ParseMoveStringList(Board, firstLine) : null; break; case EngineCommand.BestMove: // Update the target move (and potentially auto-play it) ProcessBestMove(lastLine, true); TryStopTimedCommand(); break; case EngineCommand.Options: string[] optionLines = new string[outputLines.Length]; Array.Copy(outputLines, optionLines, optionLines.Length); EngineOptions.ParseEngineOptionLines(optionLines); break; case EngineCommand.Info: ID = firstLine.StartsWith("id ") ? firstLine.Substring(3).Trim() : "Unknown"; EngineCapabilities = new EngineCapabilities(lastLine); break; default: break; } }