/// <summary> /// Ki2形式の複数の指し手をパースします。 /// </summary> private IEnumerable <KifMoveNode> ParseMoveLinesKi2() { while (this.currentLine != null) { var smove = KifUtil.ParseSpecialMove(this.currentLine); if (smove != null) { yield return(new KifMoveNode { Move = smove, }); } var c = this.currentLine[0]; if (c != '▲' && c != '△' && c != '▼' && c != '▽') { ReadNextLine(); continue; } var list = ParseMoveLineKi2(this.currentLine); foreach (var move in list) { yield return(new KifMoveNode { Move = move, }); } ReadNextLine(); } }
/// <summary> /// コメント行を読み込み、それをノードに設定します。 /// </summary> /// <remarks> /// コメント行は複数行に渡ることがあるため、 /// コメント行がある間はずっとコメントを追加し続けます。 /// </remarks> private void ReadCommentLines(KifMoveNode node, bool forceOneLine) { var alreadyRead = false; while (this.currentLine != null) { var commentData = KifUtil.ParseCommentLine(this.currentLine); if (commentData == null) { if (forceOneLine && !alreadyRead) { ReadNextLine(); } break; } // 必要なコメントのみ棋譜から取り出します。 if (commentData.IsMoveComment) { node.AddComment(commentData.Comment); } ReadNextLine(); alreadyRead = true; } }
/// <summary> /// コメント行かどうか調べます。 /// </summary> private bool IsCommentLine(string line) { if (line == null) { throw new ArgumentNullException("line"); } return(KifUtil.IsCommentLine(line)); }
/// <summary> /// ヘッダ部分を出力します。 /// </summary> private void WriteHeader(TextWriter writer, KifuObject kifu) { writer.WriteLine("# ---- 棋譜ファイル ----"); foreach (var item in kifu.Header) { var name = KifUtil.GetHeaderName(item.Key); if (name == null) { continue; } writer.WriteLine("{0}:{1}", name, item.Value); } }
/// <summary> /// 局面の各段をbod形式に直します。 /// </summary> private static string RankToBod(Board board, int rank) { var sb = new StringBuilder(); sb.Append("|"); // 9筋が一番左で、1筋は右になります。 for (var file = Board.BoardSize; file >= 1; --file) { sb.Append(KifUtil.PieceToStr(board[file, rank])); } sb.Append("|"); sb.Append(IntConverter.Convert(NumberType.Kanji, rank)); return(sb.ToString()); }
/// <summary> /// bod形式の文字列から、局面を読み取ります。 /// </summary> public static Board Parse(string text) { if (text == null) { throw new ArgumentNullException("text"); } var parser = new BodParser(); var lines = text.Split( new char[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries); foreach (var line in lines) { if (KifUtil.IsCommentLine(line)) { continue; } // 局面の読み取りを試みます。 if (parser.TryParse(line)) { continue; } var item = KifUtil.ParseHeaderItem(line); if (item != null) { continue; } break; } if (!parser.IsCompleted) { throw new ShogiException( "局面の読み取りを完了できませんでした。"); } return(parser.Board); }
/// <summary> /// 持ち駒を文字列に直します。 /// </summary> private static string HandToBod(Board board, BWType turn) { var list = from pieceType in EnumEx.GetValues <PieceType>() let obj = new { Piece = new Piece(pieceType, false), Count = board.GetHand(pieceType, turn), } where obj.Count > 0 select string.Format("{0}{1}{2} ", KifUtil.PieceToChar(obj.Piece), (obj.Count >= 10 ? "十" : ""), (obj.Count == 10 || obj.Count == 1 ? "" : IntConverter.Convert(NumberType.Kanji, obj.Count % 10))); var array = list.ToArray(); return(array.Any() ? string.Join("", array) : "なし"); }
/// <summary> /// Ki2形式の複数の指し手をパースします。 /// </summary> private IEnumerable <MoveNode> ParseMoveLinesKi2(Board board, out Exception error) { var nodes = new List <MoveNode>(); error = null; while (this.currentLine != null && error == null) { var smove = KifUtil.ParseSpecialMove(this.currentLine); if (smove != null) { nodes.Add(new MoveNode { Move = Move.CreateSpecialMove( smove.BWType, smove.SpecialMoveType) }); } var c = this.currentLine[0]; if (c != '▲' && c != '△' && c != '▼' && c != '▽') { ReadNextLine(); continue; } var list = ParseMoveLineKi2(board, this.currentLine, ref error); foreach (var move in list) { nodes.Add(new MoveNode { Move = move, }); } ReadNextLine(); } return(nodes); }
/// <summary> /// bod形式の各駒を読み取ります。 /// </summary> private BoardPiece ParsePiece(int file, int rank, string line) { var index = (Board.BoardSize - file) * 2 + 1; var pieceStr = line.Substring(index, 2); var piece = KifUtil.StrToPiece(pieceStr); if (piece == null) { throw new ShogiException( string.Format( "局面の{0}段目の駒'{1}'が正しくありません。", rank, pieceStr)); } if (piece.PieceType == PieceType.None) { return(null); } return(piece); }
/// <summary> /// 持ち駒の各駒をパースします。 /// </summary> /// <remarks> /// 各駒文字を最初の漢字で表し、後に続く漢数字でその数を示します。 /// </remarks> private Tuple <PieceType, int> ParseHandPiece(BWType bwType, string handPieceText) { // 駒の種類を取得します。 var piece = KifUtil.CharToPiece(handPieceText[0]); if (piece == null) { throw new ShogiException( string.Format( "{0}手の持ち駒'{1}'が正しくありません。", bwType == BWType.Black ? "先" : "後", handPieceText)); } if (piece.IsPromoted) { throw new ShogiException( string.Format( "{0}手の持ち駒に成り駒があります。", bwType == BWType.Black ? "先" : "後")); } // 駒の数指定があれば、それを解析します。 int count = 1; if (handPieceText.Length > 1) { var numText = handPieceText.Substring(1); var normalized = StringNormalizer.NormalizeNumber(numText, true); count = int.Parse(normalized); } return(Tuple.Create(piece.PieceType, count)); }
/// <summary> /// ヘッダー行をパースします。 /// </summary> private bool ParseHeaderLine(string line, BodParser parser, KifuHeader header = null, KifMoveNode head = null) { if (line == null) { // ファイルの終了を意味します。 return(false); } var commentData = KifUtil.ParseCommentLine(line); if (commentData != null) { // コメントはパース結果に含めます。 if (head != null && commentData.IsMoveComment) { head.AddComment(commentData.Comment); } return(true); } // 読み飛ばすべき説明行 if (line.Contains("手数----指手---------消費時間")) { this.isKif = true; // kif形式です。 return(true); } // 局面の読み取りを試みます。 if (parser.TryParse(line)) { return(true); } var item = KifUtil.ParseHeaderItem(line); if (item != null) { if (item.Key == "手合割" && item.Value != "その他") { this.startBoard = BoardTypeUtil.CreateBoardFromName(item.Value); } if (header != null) { // 可能ならヘッダアイテムを設定します。 var type = KifUtil.GetHeaderType(item.Key); if (type != null) { header[type.Value] = item.Value; } } return(true); } // ヘッダが正しく読めない場合、 // 区切りなしに指し手行に入っている可能性があります。 if (MoveLineRegex.IsMatch(line)) { this.isKif = true; } return(false); }
/// <summary> /// 局面を示す各行の読み込みを試行します。 /// </summary> public bool TryParse(string line) { if (string.IsNullOrEmpty(line)) { throw new ArgumentNullException("line"); } if (IsBoardParsing) { ParseBoardLine(line); return(true); } // 局面の解析 if (line.Contains("9 8 7 6 5 4 3 2 1")) { this.state = 1; return(true); } // 手数 if (Regex.IsMatch(line, @"^\s*手数=")) { // 手数は無視します。 return(true); } // 手番 if (Regex.IsMatch(line, @"^\s*(下手|先手)番")) { SetTurn(BWType.Black); return(true); } if (Regex.IsMatch(line, @"^\s*(上手|後手)番")) { SetTurn(BWType.White); return(true); } // ヘッダの解析 var item = KifUtil.ParseHeaderItem(line); if (item != null) { if (item.Key.Contains("上手") || item.Key.Contains("下手")) { SetHandicapTurn(); } // 持駒の解析 if (item.Key == "先手の持駒" || item.Key == "下手の持駒") { ParseHand(BWType.Black, item.Value); return(true); } if (item.Key == "後手の持駒" || item.Key == "上手の持駒") { ParseHand(BWType.White, item.Value); return(true); } } return(false); }