/// <summary> /// 各段の駒文字を返します。 /// </summary> private static IEnumerable <string> RankToSfen(Board board, int rank) { var spaceCount = 0; for (var file = Board.BoardSize; file >= 1; --file) { var piece = board[file, rank]; if (piece == null) { // 駒がない場合 spaceCount += 1; } else { // 駒がある場合 if (spaceCount > 0) { yield return(spaceCount.ToString()); spaceCount = 0; } yield return(SfenUtil.PieceToSfen(piece)); } } // 空白の数は数字で示します。 if (spaceCount > 0) { yield return(spaceCount.ToString()); } }
/// <summary> /// 持ち駒を読み込みます。 /// </summary> private static void ParseHand(Board board, string sfen) { if (sfen[0] == '-') { // 何もする必要がありません。 return; } var count = 0; foreach (var c in sfen) { if ('0' <= c && c <= '9') { // 10と数字が並ぶ可能性があります。 count = count * 10 + (c - '0'); } else { var piece = SfenUtil.SfenToPiece(c); if (piece == null) { throw new SfenException( "SFEN形式の持ち駒'" + c + "'が正しくありません。"); } // 持ち駒の数は0以上に合わせます。 var pcount = Math.Max(count, 1); board.SetHand(piece.PieceType, piece.BWType, pcount); count = 0; } } }
/// <summary> /// 持ち駒を読み込みます。 /// </summary> private static void ParseHand(Board board, string sfen) { if (sfen[0] == '-') { // 何もする必要がありません。 return; } var count = 1; foreach (var c in sfen) { if ('1' <= c && c <= '9') { count = c - '0'; } else { var piece = SfenUtil.SfenToPiece(c); if (piece == null) { throw new SfenException( "SFEN形式の持ち駒'" + c + "'が正しくありません。"); } board.SetHand(piece.PieceType, piece.BWType, count); count = 1; } } }
/// <summary> /// 指し手をSFEN形式の文字列に変換します。 /// </summary> /// <remarks> /// 筋に関しては1から9までの数字で表記され、段に関してはaからiまでの /// アルファベット(1段目がa、2段目がb、・・・、9段目がi) /// というように表記されます。位置の表記はこの2つを組み合わせ、 /// 5一なら5a、1九なら1iとなります。 /// /// 指し手に関しては、駒の移動元の位置と移動先の位置を並べて書きます。 /// 7七の駒が7六に移動したのであれば、7g7fと表記します。 /// (駒の種類を表記する必要はありません。) /// 駒が成るときは最後に+を追加します。8八の駒が2二に移動して /// 成るなら8h2b+です。 /// 持ち駒を打つときは "[駒の種類(大文字)]*[打った場所]" となります。 /// 金を5二に打つ場合はG*5bとなります /// </remarks> public static string ToSfen(this Move move) { if (move == null) { throw new ArgumentNullException("move"); } if (!move.Validate()) { throw new ArgumentException("move"); } if (move.IsSpecialMove) { // 投了などの特殊な指し手 throw new SfenException( move + ": sfenに変換することはできません。"); } var dstFile = move.DstSquare.File; var dstRank = (char)((int)'a' + (move.DstSquare.Rank - 1)); if (move.ActionType == ActionType.Drop) { // 駒打ちの場合 var piece = SfenUtil.PieceTypeToSfen(move.DropPieceType); return(string.Format("{0}*{1}{2}", piece, dstFile, dstRank)); } else { // 駒の移動の場合 var srcFile = move.SrcSquare.File; var srcRank = (char)((int)'a' + (move.SrcSquare.Rank - 1)); var isPromote = (move.ActionType == ActionType.Promote); return(string.Format("{0}{1}{2}{3}{4}", srcFile, srcRank, dstFile, dstRank, (isPromote ? "+" : ""))); } }
/// <summary> /// 持ち駒をSFEN形式に変換します。 /// </summary> private static string HandToSfen(Board board) { var handList = from turn in new BWType[] { BWType.Black, BWType.White } from pieceType in EnumEx.GetValues <PieceType>() let obj = new { Piece = new BoardPiece(pieceType, false, turn), Count = board.GetHand(pieceType, turn), } where obj.Count > 0 select string.Format("{0}{1}", (obj.Count > 1 ? obj.Count.ToString() : ""), SfenUtil.PieceToSfen(obj.Piece)); // ToArray()しないと、MONOでstring.Joinのコンパイルに失敗します。 var array = handList.ToArray(); return(array.Any() ? string.Join("", array) : "-"); }
/// <summary> /// 局面を読み込みます。 /// </summary> private static void ParseBoard0(Board board, string sfen) { var rank = 1; var file = 9; var promoted = false; foreach (var c in sfen) { if (rank > 9) { throw new SfenException( "局面の段数が9を超えます。"); } if (c == '/') { if (file != 0) { throw new SfenException( "SFEN形式の" + rank + "段の駒数が合いません。"); } rank += 1; file = 9; promoted = false; } else if (c == '+') { promoted = true; } else if ('1' <= c && c <= '9') { file -= (c - '0'); promoted = false; } else { if (file < 1) { throw new SfenException( "SFEN形式の" + rank + "段の駒数が多すぎます。"); } var piece = SfenUtil.SfenToPiece(c); if (piece == null) { throw new SfenException( "SFEN形式の駒'" + c + "'が正しくありません。"); } var type = piece.PieceType; if (promoted && type != PieceType.Gyoku && type != PieceType.Kin) { piece = new BoardPiece(piece.PieceType, promoted, piece.BWType); } board[file, rank] = piece; file -= 1; promoted = false; } } if (file != 0) { throw new SfenException( "SFEN形式の" + rank + "段の駒数が合いません。"); } }
/// <summary> /// SFEN形式の指し手を、指し手に変換します。 /// </summary> public static Move SfenToMove(this Board board, string sfen) { if (board == null) { throw new ArgumentNullException("board"); } if (string.IsNullOrEmpty(sfen)) { throw new ArgumentNullException("sfen"); } if (sfen.Length < 4) { return(null); } var dropPieceType = SfenUtil.SfenToPieceType(sfen[0]); if (dropPieceType != PieceType.None) { // 駒打ちの場合 if ((sfen[1] != '*') || (sfen[2] < '1' || '9' < sfen[2]) || (sfen[3] < 'a' || 'i' < sfen[3])) { return(null); } var dstFile = (sfen[2] - '1') + 1; var dstRank = (sfen[3] - 'a') + 1; return(Move.CreateDrop( board.Turn, new Square(dstFile, dstRank), dropPieceType)); } else { // 駒の移動の場合 if ((sfen[0] < '1' || '9' < sfen[0]) || (sfen[2] < '1' || '9' < sfen[2]) || (sfen[1] < 'a' || 'i' < sfen[1]) || (sfen[3] < 'a' || 'i' < sfen[3])) { return(null); } var srcFile = (sfen[0] - '1') + 1; var srcRank = (sfen[1] - 'a') + 1; var dstFile = (sfen[2] - '1') + 1; var dstRank = (sfen[3] - 'a') + 1; var piece = board[srcFile, srcRank]; if (piece == null) { return(null); } var promote = (sfen.Length > 4 && sfen[4] == '+'); return(Move.CreateMove( board.Turn, new Square(srcFile, srcRank), new Square(dstFile, dstRank), piece.Piece, promote, BoardPiece.GetPiece(board[dstFile, dstRank]))); } }