using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;

namespace shuchancontrol
{
    /// <summary>
    /// shuchanに特化したチェス盤の表現クラス。
    /// </summary>
    public class ShuchanChessBoard : ICloneable
    {
        /// <summary>
        /// ボードのランクとファイルの総数。
        /// </summary>
        const int NRank = 8, NFile = 8;

        /// <summary>
        /// 補助駒(A-H)の数。
        /// </summary>
        const int NExtraPiece = 8;

        #region 駒種の定義
        public enum PieceType
        {
            None = 0,
            King = 1,
            Queen = 2,
            Rook = 3,
            Bishop = 4,
            Knight = 5,
            Pawn = 6,
        }

        public enum PieceColor
        {
            White = 0,
            Black = 1,
        }

        public struct Piece : IEquatable<Piece>
        {
            PieceType _type;
            PieceColor _color;
            /// <summary>
            /// 駒の種類。
            /// </summary>
            public PieceType Type
            {
                get
                {
                    return _type;
                }
                set
                {
                    _type = value;
                }
            }
            /// <summary>
            /// 駒の色。
            /// </summary>
            public PieceColor Color
            {
                get
                {
                    return _color;
                }
                set
                {
                    _color = value;
                }
            }

            /// <summary>
            /// 駒が無いことを表すインスタンスを返す。
            /// </summary>
            public static Piece None
            {
                get
                {
                    return new Piece(PieceType.None, PieceColor.White);
                }
            }

            #region IEquatable<Piece> メンバ

            public bool Equals(Piece other)
            {
                if (this.Type == PieceType.None)
                {
                    if (other.Type == PieceType.None)
                    {
                        //None同士は色が違っても等しい。
                        return true;
                    }
                }
                return (this.Type == other.Type) & (this.Color == other.Color);
            }

            #endregion

            public static bool operator ==(Piece x, Piece y)
            {
                return x.Equals(y);
            }

            public static bool operator !=(Piece x, Piece y)
            {
                return !x.Equals(y);
            }

            public override bool Equals(object obj)
            {
                if (!(obj is Piece))
                {
                    return false;
                }

                return this.Equals((Piece)obj);
            }

            public override int GetHashCode()
            {
                if (this.Type == PieceType.None)
                {
                    return 0;
                }

                return (int)this.Type * 2 + (int)this.Color;
            }

            public Piece(PieceType type, PieceColor color)
            {
                _type = type;
                _color = color;
            }

            /// <summary>
            /// このインスタンスが駒の無い状態を表すようにする。
            /// </summary>
            public void SetNone()
            {
                _type = PieceType.None;
                _color = PieceColor.White;
            }

            /// <summary>
            /// 駒を表す1文字を返す。Type == PieceType.Noneのときはスペースが返る。
            /// </summary>
            /// <returns></returns>
            public override string ToString()
            {
                char piecechar;
                switch (Type)
                {
                    case PieceType.King:
                        piecechar = 'K';
                        break;
                    case PieceType.Queen:
                        piecechar = 'Q';
                        break;
                    case PieceType.Rook:
                        piecechar = 'R';
                        break;
                    case PieceType.Bishop:
                        piecechar = 'B';
                        break;
                    case PieceType.Knight:
                        piecechar = 'N';
                        break;
                    case PieceType.Pawn:
                        piecechar = 'P';
                        break;
                    default:
                        piecechar = ' ';
                        break;
                }
                if (Color == PieceColor.Black)
                {
                    piecechar = char.ToLower(piecechar);
                }

                return piecechar.ToString();
            }

        }
        #endregion

        #region 盤状態
        /// <summary>
        /// 駒の配置状態。[ランク(0-7), ファイル(0-7)]で設定。
        /// </summary>
        Piece[,] board = new Piece[NRank, NFile];

        /// <summary>
        /// 次に動かすのが白のときtrue。
        /// </summary>
        public bool WhiteOnMove { get; set; }

        /// <summary>
        /// WhiteOnMoveの反転。
        /// </summary>
        public bool BlackOnMove
        {
            get
            {
                return !WhiteOnMove;
            }
            set
            {
                WhiteOnMove = !value;
            }
        }

        #region CastlingAvailability
        /// <summary>
        /// 白のキングサイドキャスリングが可能。
        /// </summary>
        public bool CastlingAvailabilityWhiteKingSide { get; set; }

        /// <summary>
        /// 白のクイーンサイドキャスリングが可能。
        /// </summary>
        public bool CastlingAvailabilityWhiteQueenSide { get; set; }

        /// <summary>
        /// 黒のキングサイドキャスリングが可能。
        /// </summary>
        public bool CastlingAvailabilityBlackKingSide { get; set; }

        /// <summary>
        /// 黒のクイーンサイドキャスリングが可能。
        /// </summary>
        public bool CastlingAvailabilityBlackQueenSide { get; set; }
        #endregion

        /// <summary>
        /// 次の手でアンパッサン可能な座標。不可能なときはnull。
        /// </summary>
        public ShuchanPiecePosition EnpassantPosition { get; set; }

        /// <summary>
        /// ポーン以外が移動するごとに1増加する(白黒の移動それぞれで1増加)。ポーンが動いたとき0にリセットされる。
        /// </summary>
        public ushort HalfmoveClock { get; set; }

        /// <summary>
        /// 黒が動き終わったときに1増加する。初期値は1。
        /// </summary>
        public ushort FullmoveNumber { get; set; }
        #endregion

        #region Shuchan特有の情報
        /// <summary>
        /// 駒の動きを検出する補助プログラムの実行ファイル名。
        /// </summary>
        public string MoveComputingHelperExe { get; set; }
        /// <summary>
        /// 駒の動きを検出する補助プログラムの追加引数。
        /// </summary>
        public string MoveComputingHelperArgument { get; set; }

        /// <summary>
        /// 白側の予備駒(A-H)の割り当て記録。未割り当てのものはPieceType.None。
        /// </summary>
        public PieceType[] ExtraPieceWhite { get; set; }

        /// <summary>
        /// 黒側の予備駒(A-H)の割り当て記録。未割り当てのものはPieceType.None。
        /// </summary>
        public PieceType[] ExtraPieceBlack { get; set; }
        #endregion

        #region Constructor
        public ShuchanChessBoard(bool defaultposition)
        {
            WhiteOnMove = true;
            CastlingAvailabilityBlackKingSide = true;
            CastlingAvailabilityBlackQueenSide = true;
            CastlingAvailabilityWhiteKingSide = true;
            CastlingAvailabilityWhiteQueenSide = true;
            EnpassantPosition = null;
            HalfmoveClock = 0;
            FullmoveNumber = 0;

            ExtraPieceWhite = new PieceType[NExtraPiece];
            ExtraPieceBlack = new PieceType[NExtraPiece];
            for (int i = 0; i < ExtraPieceWhite.Length; i++)
            {
                ExtraPieceWhite[i] = PieceType.None;
            }
            for (int i = 0; i < ExtraPieceBlack.Length; i++)
            {
                ExtraPieceBlack[i] = PieceType.None;
            }

            //まず駒の無い状態に初期化する。
            for (int rank = 0; rank < NRank; rank++)
            {
                for (int file = 0; file < NFile; file++)
                {
                    board[rank, file] = new Piece(PieceType.None, PieceColor.White);
                }
            }

            if (defaultposition)
            {
                //チェスのルールの初期配置に従って駒を配置する。
                #region 駒の初期配置
                board[0, 0] = new Piece(PieceType.Rook, PieceColor.White);
                board[0, 1] = new Piece(PieceType.Knight, PieceColor.White);
                board[0, 2] = new Piece(PieceType.Bishop, PieceColor.White);
                board[0, 3] = new Piece(PieceType.Queen, PieceColor.White);
                board[0, 4] = new Piece(PieceType.King, PieceColor.White);
                board[0, 5] = new Piece(PieceType.Bishop, PieceColor.White);
                board[0, 6] = new Piece(PieceType.Knight, PieceColor.White);
                board[0, 7] = new Piece(PieceType.Rook, PieceColor.White);
                board[1, 0] = new Piece(PieceType.Pawn, PieceColor.White);
                board[1, 1] = new Piece(PieceType.Pawn, PieceColor.White);
                board[1, 2] = new Piece(PieceType.Pawn, PieceColor.White);
                board[1, 3] = new Piece(PieceType.Pawn, PieceColor.White);
                board[1, 4] = new Piece(PieceType.Pawn, PieceColor.White);
                board[1, 5] = new Piece(PieceType.Pawn, PieceColor.White);
                board[1, 6] = new Piece(PieceType.Pawn, PieceColor.White);
                board[1, 7] = new Piece(PieceType.Pawn, PieceColor.White);
                board[6, 0] = new Piece(PieceType.Pawn, PieceColor.Black);
                board[6, 1] = new Piece(PieceType.Pawn, PieceColor.Black);
                board[6, 2] = new Piece(PieceType.Pawn, PieceColor.Black);
                board[6, 3] = new Piece(PieceType.Pawn, PieceColor.Black);
                board[6, 4] = new Piece(PieceType.Pawn, PieceColor.Black);
                board[6, 5] = new Piece(PieceType.Pawn, PieceColor.Black);
                board[6, 6] = new Piece(PieceType.Pawn, PieceColor.Black);
                board[6, 7] = new Piece(PieceType.Pawn, PieceColor.Black);
                board[7, 0] = new Piece(PieceType.Rook, PieceColor.Black);
                board[7, 1] = new Piece(PieceType.Knight, PieceColor.Black);
                board[7, 2] = new Piece(PieceType.Bishop, PieceColor.Black);
                board[7, 3] = new Piece(PieceType.Queen, PieceColor.Black);
                board[7, 4] = new Piece(PieceType.King, PieceColor.Black);
                board[7, 5] = new Piece(PieceType.Bishop, PieceColor.Black);
                board[7, 6] = new Piece(PieceType.Knight, PieceColor.Black);
                board[7, 7] = new Piece(PieceType.Rook, PieceColor.Black);
                #endregion
            }
        }

        public ShuchanChessBoard(ShuchanChessBoard template)
            : this(false)
        {
            WhiteOnMove = template.WhiteOnMove;
            CastlingAvailabilityBlackKingSide = template.CastlingAvailabilityBlackKingSide;
            CastlingAvailabilityBlackQueenSide = template.CastlingAvailabilityBlackQueenSide;
            CastlingAvailabilityWhiteKingSide = template.CastlingAvailabilityWhiteKingSide;
            CastlingAvailabilityWhiteQueenSide = template.CastlingAvailabilityWhiteQueenSide;
            EnpassantPosition = template.EnpassantPosition;
            HalfmoveClock = template.HalfmoveClock;
            FullmoveNumber = template.FullmoveNumber;
            MoveComputingHelperExe = template.MoveComputingHelperExe;
            MoveComputingHelperArgument = template.MoveComputingHelperArgument;

            Array.Copy(template.board, this.board, template.board.Length);
            Array.Copy(template.ExtraPieceWhite, this.ExtraPieceWhite, template.ExtraPieceWhite.Length);
            Array.Copy(template.ExtraPieceBlack, this.ExtraPieceBlack, template.ExtraPieceBlack.Length);
        }
        #endregion

        #region ICloneable メンバ

        public object Clone()
        {
            return new ShuchanChessBoard(this);
        }

        #endregion

        #region FEN関係
        /// <summary>
        /// 現在のインスタンスの表す盤状態をFEN表記で取得する。
        /// </summary>
        /// <returns></returns>
        public string GetFEN()
        {
            //FEN表記については
            //http://www.chessville.com/Reference_Center/FEN_Description.htm
            //を参考にした。

            StringBuilder fen = new StringBuilder();
            //駒表記部分
            StringBuilder pieceposition = new StringBuilder();
            //ランク8から1の順
            for (int rank = NRank - 1; rank >= 0; rank--)
            {
                for (int file = 0; file < NFile; file++)
                {
                    pieceposition.Append(piece2FENChar(board[rank, file]));
                }

                if (rank != 0)
                {
                    //最終ランク以外では"/"を入れる。
                    pieceposition.Append('/');
                }
            }

            fen.Append(packFen(pieceposition.ToString()));
            fen.Append(' ');

            //手番側表記
            fen.Append(WhiteOnMove ? 'w' : 'b');
            fen.Append(' ');

            //キャスリング可能性表記
            StringBuilder castavail = new StringBuilder();
            if (CastlingAvailabilityWhiteKingSide)
            {
                castavail.Append('K');
            }
            if (CastlingAvailabilityWhiteQueenSide)
            {
                castavail.Append('Q');
            }
            if (CastlingAvailabilityBlackKingSide)
            {
                castavail.Append('k');
            }
            if (CastlingAvailabilityBlackQueenSide)
            {
                castavail.Append('q');
            }
            if (castavail.Length == 0)
            {
                //どのキャスリングも不可能なとき
                castavail.Append("-");
            }

            fen.Append(castavail.ToString());
            fen.Append(' ');

            //アンパッサン位置表記
            if (EnpassantPosition != null)
            {
                fen.Append(EnpassantPosition.ToString());
            }
            else
            {
                fen.Append('-');
            }
            fen.Append(' ');

            //ポーン以外ハーフムーブ回数表記
            fen.Append(HalfmoveClock.ToString());
            fen.Append(' ');

            //フルムーブ回数表記
            fen.Append(FullmoveNumber.ToString());

            return fen.ToString();
        }

        /// <summary>
        /// FEN表記の駒の配置部分のみを入力し、このインスタンスの駒の配置を設定する。
        /// </summary>
        /// <param name="piecearrayofFEN">FENの内の最初のセクションの駒の配置を記述する部分。</param>
        void setFENPieceArray(string piecearrayofFEN)
        {
            string[] pieceranks = unpackFen(piecearrayofFEN).Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
            if (pieceranks.Length != NRank)
            {
                throw new ArgumentException();
            }
            Array.Reverse(pieceranks);  //FENではランクが8から1の順に並んでいるので逆順にする。
            for (int rank = 0; rank < NRank; rank++)
            {
                string rankline = pieceranks[rank];
                if (rankline.Length != NFile)
                {
                    throw new ArgumentException();
                }

                for (int file = 0; file < NFile; file++)
                {
                    Piece piece;
                    if (fenChar2Piece(rankline[file], out piece))
                    {
                        board[rank, file] = piece;
                    }
                    else
                    {
                        throw new ArgumentException();
                    }
                }
            }

        }

        /// <summary>
        /// 現在のインスタンスの表す盤状態をFEN表記で設定する。
        /// </summary>
        /// <param name="fen"></param>
        public void SetFen(string fen)
        {
            if (string.IsNullOrEmpty(fen))
            {
                throw new ArgumentNullException();
            }

            fen = fen.Trim();
            string[] parts = fen.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
            if (parts.Length != 6)
            {
                throw new ArgumentException();
            }

            //駒表記部分
            setFENPieceArray(parts[0]);

            //手番側表記
            switch (parts[1])
            {
                case "w":
                    WhiteOnMove = true;
                    break;
                case "b":
                    WhiteOnMove = false;
                    break;
                default:
                    throw new ArgumentException();
            }

            //キャスリング可能性表記
            if (parts[2].Contains("K"))
            {
                CastlingAvailabilityWhiteKingSide = true;
            }
            if (parts[2].Contains("Q"))
            {
                CastlingAvailabilityWhiteQueenSide = true;
            }
            if (parts[2].Contains("k"))
            {
                CastlingAvailabilityBlackKingSide = true;
            }
            if (parts[2].Contains("q"))
            {
                CastlingAvailabilityBlackQueenSide = true;
            }

            //アンパッサン位置表記
            if (parts[3] != "-")
            {
                EnpassantPosition = new ShuchanPiecePosition(parts[3]);
            }

            //ポーン以外ハーフムーブ回数表記
            HalfmoveClock = ushort.Parse(parts[4]);

            //フルムーブ回数表記
            FullmoveNumber = ushort.Parse(parts[5]);
        }

        #region FEN記号変換
        /// <summary>
        /// 駒をFENの文字に変換する。
        /// </summary>
        /// <param name="type"></param>
        /// <param name="color"></param>
        /// <returns></returns>
        static char piece2FENChar(PieceType type, PieceColor color)
        {
            char c;
            switch (type)
            {
                case PieceType.King:
                    c = 'k';
                    break;
                case PieceType.Queen:
                    c = 'q';
                    break;
                case PieceType.Rook:
                    c = 'r';
                    break;
                case PieceType.Bishop:
                    c = 'b';
                    break;
                case PieceType.Knight:
                    c = 'n';
                    break;
                case PieceType.Pawn:
                    c = 'p';
                    break;
                case PieceType.None:
                default:
                    c = ' ';
                    break;
            }

            //白は大文字
            if (color == PieceColor.White)
            {
                c = Char.ToUpper(c);
            }

            return c;
        }

        static char piece2FENChar(Piece piece)
        {
            return piece2FENChar(piece.Type, piece.Color);
        }

        /// <summary>
        /// FEN表記の文字を駒に変換する。
        /// </summary>
        /// <param name="c"></param>
        /// <param name="type"></param>
        /// <param name="color"></param>
        /// <returns>変換に成功すればtrue。範囲外の文字ならfalse。</returns>
        static bool fenChar2Piece(char c, out PieceType type, out PieceColor color)
        {
            type = PieceType.None;
            color = PieceColor.White;

            if (c == ' ')
            {
                return true;
            }

            if (!Char.IsLetter(c))
            {
                return false;
            }

            if (Char.IsLower(c))
            {
                color = PieceColor.Black;
            }
            else
            {
                color = PieceColor.White;
            }

            switch (Char.ToLower(c))
            {
                case 'k':
                    type = PieceType.King;
                    break;
                case 'q':
                    type = PieceType.Queen;
                    break;
                case 'r':
                    type = PieceType.Rook;
                    break;
                case 'b':
                    type = PieceType.Bishop;
                    break;
                case 'n':
                    type = PieceType.Knight;
                    break;
                case 'p':
                    type = PieceType.Pawn;
                    break;
                default:
                    return false;
            }

            return true;
        }

        static bool fenChar2Piece(char c, out Piece piece)
        {
            bool result;
            PieceType type;
            PieceColor color;
            result = fenChar2Piece(c, out type, out color);
            piece = new Piece(type, color);
            return result;
        }

        /// <summary>
        /// FENの駒位置表示にみられるスペースの省略表記を解凍してスペースに直す。
        /// </summary>
        /// <param name="fen"></param>
        /// <returns></returns>
        static string unpackFen(string fen)
        {
            //1から8の数字をその数ぶんのスペースに直す。
            for (int i = 1; i <= 8; i++)
            {
                string spacestr = "";
                for (int j = 0; j < i; j++)
                {
                    spacestr += " ";
                }

                fen = fen.Replace(i.ToString(), spacestr);
            }

            return fen;
        }

        /// <summary>
        /// FENの駒位置表示のスペースを数字を使った省略表記に変換する。
        /// </summary>
        /// <param name="fen"></param>
        /// <returns></returns>
        static string packFen(string fen)
        {
            //1から8個連続したスペースを相応の数字に変換する。
            for (int i = 8; i >= 1; i--)
            {
                string spacestr = "";
                for (int j = 0; j < i; j++)
                {
                    spacestr += " ";
                }

                fen = fen.Replace(spacestr, i.ToString());
            }

            return fen;
        }
        #endregion
        #endregion

        public class MoveFromRecogFENResult
        {
            /// <summary>
            /// 移動の棋譜表記。
            /// 形式は"e2e4"のような移動前・移動後を並べた表記。
            /// プロモーションを含む場合は"e7e8q"のように末尾に変化先の駒の文字(q,r,b,n)を追加する。
            /// </summary>
            public string MoveNotation { get; set; }
            /// <summary>
            /// 移動のSAN表記。
            /// </summary>
            public string MoveSAN { get; set; }
            /// <summary>
            /// このインスタンスが駒移動を示しているかどうか。
            /// ただし、この移動がチェスのすべてのルールを満たしていることは保障されない。
            /// </summary>
            public bool IsValid { get; set; }
            /// <summary>
            /// 正しい移動であると認識されなかった場合の理由。
            /// </summary>
            public string ErrorMessage { get; set; }
            /// <summary>
            /// プロモーション先の駒を指定して再度呼び出す必要があることを示す。
            /// </summary>
            public bool PromotionTargetRequired { get; set; }
            /// <summary>
            /// プロモーションがあったとき、それに使用される実際の駒(A-H)。プロモーションが無かったときは' '。
            /// </summary>
            public char PromotedToRealPiece { get; set; }
            public ShuchanPiecePosition MoveFrom { get; set; }
            public ShuchanPiecePosition MoveTo { get; set; }
            public bool IsCastling { get; set; }
            public bool IsPromotion { get; set; }
            public PieceType PromotedTo { get; set; }

            public MoveFromRecogFENResult()
            {
                this.IsValid = false;
                this.MoveNotation = null;
                this.PromotionTargetRequired = false;
                this.ErrorMessage = null;
                this.PromotedToRealPiece = ' ';
                this.MoveSAN = null;
            }
        }

        /// <summary>
        /// 移動後のRecogFENを入力することで手を打つ。移動が有効にしろ無効にしろこのインスタンスの内容が変化することに注意。
        /// </summary>
        /// <param name="recogFEN">画像認識して生成した移動後の状態のRecogFEN。</param>
        /// <param name="promotion">ポーンがプロモーションした場合のプロモーション先。プロモーションしていなければ何でもよい。</param>
        /// <returns>移動の内容。</returns>
        public MoveFromRecogFENResult MoveFromRecogFEN(string recogFEN, PieceType promotion)
        {
            MoveFromRecogFENResult result = new MoveFromRecogFENResult();

            //まず追加駒を普通の駒に直して、新しい盤面を構築する。
            //RecogFENではビショップが"I"なのに注意。

            //新しい追加駒が使われていないかチェックする。
            bool promotionoccured = false;  //プロモーションが発生したときtrue。
            #region 新規追加駒チェック
            char nextextrapiecechar = extraPieceIndex2Char(extraPieceNextIndex(WhiteOnMove), WhiteOnMove);
            if (recogFEN.Contains(nextextrapiecechar.ToString()))
            {
                //新しい駒が使われている。
                if (
                    (promotion == PieceType.Rook) |
                    (promotion == PieceType.Queen) |
                    (promotion == PieceType.Knight) |
                    (promotion == PieceType.Bishop))
                {
                    promotionoccured = true;
                    if (WhiteOnMove)
                    {
                        ExtraPieceWhite[extraPieceNextIndex(WhiteOnMove)] = promotion;
                    }
                    else
                    {
                        ExtraPieceBlack[extraPieceNextIndex(WhiteOnMove)] = promotion;
                    }
                    result.PromotedToRealPiece = nextextrapiecechar;
                }
                else
                {
                    //プロモーション先が指定されていない。
                    result.PromotionTargetRequired = true;
                    return result;
                }
            }
            #endregion
            //追加駒を通常の駒に置き換えていく。
            #region 追加駒置換
            //白サイド
            for (int i = 0; i < NExtraPiece; i++)
            {
                char extrapiecechar = extraPieceIndex2Char(i, true);
                PieceType normalpiecetype = ExtraPieceWhite[i];
                char normalpiecechar;
                switch (normalpiecetype)
                {
                    case PieceType.Queen:
                        normalpiecechar = 'Q';
                        break;
                    case PieceType.Rook:
                        normalpiecechar = 'R';
                        break;
                    case PieceType.Bishop:
                        normalpiecechar = 'I';
                        break;
                    case PieceType.Knight:
                        normalpiecechar = 'N';
                        break;
                    default:
                        //これがrecogFEN内に発生した場合は追加駒の使い方が間違っていることになる。
                        normalpiecechar = 'X';
                        break;
                }
                recogFEN = recogFEN.Replace(extrapiecechar, normalpiecechar);
            }
            //黒サイド
            for (int i = 0; i < NExtraPiece; i++)
            {
                char extrapiecechar = extraPieceIndex2Char(i, false);
                PieceType normalpiecetype = ExtraPieceBlack[i];
                char normalpiecechar;
                switch (normalpiecetype)
                {
                    case PieceType.Queen:
                        normalpiecechar = 'q';
                        break;
                    case PieceType.Rook:
                        normalpiecechar = 'r';
                        break;
                    case PieceType.Bishop:
                        normalpiecechar = 'i';
                        break;
                    case PieceType.Knight:
                        normalpiecechar = 'n';
                        break;
                    default:
                        //これがrecogFEN内に発生した場合は追加駒の使い方が間違っていることになる。
                        normalpiecechar = 'X';
                        break;
                }
                recogFEN = recogFEN.Replace(extrapiecechar, normalpiecechar);
            }
            #endregion
            //ビショップをiからbに置き換え、普通のFENの駒部分を完成させる。
            string piecearrayoffen = recogFEN.Replace('I', 'B').Replace('i', 'b');
            ShuchanChessBoard newboard = new ShuchanChessBoard(this);   //新しい配置を含んだインスタンス(手盤の交代などは無視し、盤面を保持するものとしてのみ使う)。
            if (piecearrayoffen.Contains("X"))
            {
                //追加駒の不適切な使用。
                result.ErrorMessage = "Invalid use of extra piece.";
                return result;
            }
            try
            {
                newboard.setFENPieceArray(piecearrayoffen); //新しい盤面の配置を設定する。
            }
            catch (ArgumentException)
            {
                result.ErrorMessage = "Cannot recognize RecogFEN.";
                return result;
            }

            //変化を抽出して、駒の移動の形式へと変換する。
            //キャスリングチェック(これまでの移動やチェック状態によりキャスリングが無効であるかどうかは考慮しない、無効なときはチェスエンジンがIllegalを返すから。)
            #region キャスリングチェック
            bool castlingoccured = false;   //キャスリングがあった場合はtrueにする。
            //White-Kingside
            if (this.board[0, 4] == new Piece(PieceType.King, PieceColor.White))
            {
                if (this.board[0, 5] == Piece.None)
                {
                    if (this.board[0, 6] == Piece.None)
                    {
                        if (this.board[0, 7] == new Piece(PieceType.Rook, PieceColor.White))
                        {
                            //キャスリングを実行できる位置関係であることがわかった。
                            if (newboard.board[0, 4] == Piece.None)
                            {
                                if (newboard.board[0, 5] == new Piece(PieceType.Rook, PieceColor.White))
                                {
                                    if (newboard.board[0, 6] == new Piece(PieceType.King, PieceColor.White))
                                    {
                                        if (newboard.board[0, 7] == Piece.None)
                                        {
                                            //実際にキャスリングが行われた。
                                            result.MoveNotation = "e1g1";
                                            castlingoccured = true;
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
            //White-Queenside
            if (this.board[0, 4] == new Piece(PieceType.King, PieceColor.White))
            {
                if (this.board[0, 3] == Piece.None)
                {
                    if (this.board[0, 2] == Piece.None)
                    {
                        if (this.board[0, 1] == Piece.None)
                        {
                            if (this.board[0, 0] == new Piece(PieceType.Rook, PieceColor.White))
                            {
                                //キャスリングを実行できる位置関係であることがわかった。
                                if (newboard.board[0, 4] == Piece.None)
                                {
                                    if (newboard.board[0, 3] == new Piece(PieceType.Rook, PieceColor.White))
                                    {
                                        if (newboard.board[0, 2] == new Piece(PieceType.King, PieceColor.White))
                                        {
                                            if (newboard.board[0, 1] == Piece.None)
                                            {
                                                if (newboard.board[0, 0] == Piece.None)
                                                {
                                                    //実際にキャスリングが行われた。
                                                    result.MoveNotation = "e1c1";
                                                    castlingoccured = true;
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
            //Black-Kingside
            if (this.board[7, 4] == new Piece(PieceType.King, PieceColor.Black))
            {
                if (this.board[7, 5] == Piece.None)
                {
                    if (this.board[7, 6] == Piece.None)
                    {
                        if (this.board[7, 7] == new Piece(PieceType.Rook, PieceColor.Black))
                        {
                            //キャスリングを実行できる位置関係であることがわかった。
                            if (newboard.board[7, 4] == Piece.None)
                            {
                                if (newboard.board[7, 5] == new Piece(PieceType.Rook, PieceColor.Black))
                                {
                                    if (newboard.board[7, 6] == new Piece(PieceType.King, PieceColor.Black))
                                    {
                                        if (newboard.board[7, 7] == Piece.None)
                                        {
                                            //実際にキャスリングが行われた。
                                            result.MoveNotation = "e7g7";
                                            castlingoccured = true;
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
            //Black-Queenside
            if (this.board[7, 4] == new Piece(PieceType.King, PieceColor.Black))
            {
                if (this.board[7, 3] == Piece.None)
                {
                    if (this.board[7, 2] == Piece.None)
                    {
                        if (this.board[7, 1] == Piece.None)
                        {
                            if (this.board[7, 7] == new Piece(PieceType.Rook, PieceColor.Black))
                            {
                                //キャスリングを実行できる位置関係であることがわかった。
                                if (newboard.board[7, 4] == Piece.None)
                                {
                                    if (newboard.board[7, 3] == new Piece(PieceType.Rook, PieceColor.Black))
                                    {
                                        if (newboard.board[7, 2] == new Piece(PieceType.King, PieceColor.Black))
                                        {
                                            if (newboard.board[7, 1] == Piece.None)
                                            {
                                                if (newboard.board[7, 0] == Piece.None)
                                                {
                                                    //実際にキャスリングが行われた。
                                                    result.MoveNotation = "e7c7";
                                                    castlingoccured = true;
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
            #endregion

            //通常の移動チェック
            #region 移動チェック
            int differencecount = 0;    //移動前と後とで変化したマスの数。
            ShuchanPiecePosition movefrom = null;
            ShuchanPiecePosition moveto = null;
            for (int rank = 0; rank < NRank; rank++)
            {
                for (int file = 0; file < NFile; file++)
                {
                    Piece piecethis = this.board[rank, file];
                    Piece piecenew = newboard.board[rank, file];
                    if (piecethis != piecenew)
                    {
                        differencecount++;
                        if (piecenew == Piece.None)
                        {
                            //駒が去ったマス、すなわち移動前
                            movefrom = new ShuchanPiecePosition(rank, file);
                        }
                        else
                        {
                            //移動後のマス
                            moveto = new ShuchanPiecePosition(rank, file);
                        }
                    }
                }
            }

            if (castlingoccured)
            {
                if (differencecount != 4)
                {
                    //キャスリングなのに4マス変化する以外はおかしい。
                    result.IsValid = false;
                    result.ErrorMessage = "It seems to be castling, but the number of modified square is not 4.";
                }
                else
                {
                    result.IsValid = true;
                    //移動表記はキャスリング判定の際に代入済み。
                }
            }
            else
            {
                if (differencecount != 2)
                {
                    //通常の移動で2マス変化する以外がおかしい。
                    result.IsValid = false;
                    result.ErrorMessage = "Unrecognized move: the number of modified square is not 2.";
                }
                else
                {
                    //移動前、移動先が判明。
                    if ((this.board[movefrom.Rank, movefrom.File] == newboard.board[moveto.Rank, moveto.File])
                        | promotionoccured)
                    {
                        result.IsValid = true;
                        result.MoveNotation = movefrom.ToString() + moveto.ToString();
                        if (promotionoccured)
                        {
                            result.MoveNotation += (new Piece(promotion, PieceColor.Black)).ToString();
                        }
                    }
                    else
                    {
                        //移動元の駒と移動先の駒が違っている
                        result.IsValid = false;
                    }
                }
            }
            #endregion

            if (result.IsValid)
            {
                //移動結果のFENを取得して、インスタンスを更新する。
                Dictionary<string, string> gmiresult = new Dictionary<string, string>();
                ShuchanPiecePosition frompos, topos;
                PieceType promotionto;
                string afterfen;
                string movesan;
                bool isvalidmove;
                if (getMoveInfoFromHelpProgram(this.GetFEN(), result.MoveNotation, out isvalidmove,
                    gmiresult, out frompos, out topos, out promotionto, out afterfen, out movesan))
                {
                    result.IsValid = isvalidmove;
                    result.MoveSAN = movesan;
                    result.MoveFrom = frompos;
                    result.MoveTo = topos;
                    result.IsCastling = castlingoccured;
                    result.IsPromotion = promotionoccured;
                    result.PromotedTo = promotionto;
                    this.SetFen(afterfen);
                }
                else
                {
                    result.IsValid = false;
                    result.ErrorMessage = "Calling subprogram failed.";
                }
            }
            return result;
        }

        public class MoveByComputerResult
        {
            public class PhysicalMoveItem
            {
                public ShuchanPiecePosition From { get; set; }
                public ShuchanPiecePosition To { get; set; }

                public PhysicalMoveItem(ShuchanPiecePosition from, ShuchanPiecePosition to)
                {
                    From = from;
                    To = to;
                }

                public PhysicalMoveItem(string from, string to)
                {
                    From = new ShuchanPiecePosition(from);
                    To = new ShuchanPiecePosition(to);
                }
            }

            /// <summary>
            /// 駒を動かす順序を含めて駒の動かし方。インデックスの小さいほうの操作を先に行う。
            /// </summary>
            public List<PhysicalMoveItem> PhysicalMoves { get; set; }
            public bool TaskCompleted { get; set; }
            public string MoveSAN { get; set; }
            public bool IsCastling { get; set; }
            public bool IsPromotion { get; set; }
            public PieceType PromotedTo { get; set; }
            public ShuchanPiecePosition MoveFrom { get; set; }
            public ShuchanPiecePosition MoveTo { get; set; }

            public MoveByComputerResult()
            {
                PhysicalMoves = new List<PhysicalMoveItem>();
                TaskCompleted = false;
                MoveSAN = null;
                IsCastling = false;
                PromotedTo = PieceType.None;
            }
        }

        /// <summary>
        /// チェスエンジンが生成した手を打つ。そしてそれを盤上で実現する物理的な駒の移動手順を返す。
        /// </summary>
        /// <param name="san">SAN形式などでのコンピュータの手。"e4"や"e7e8q"など許容範囲は広い。</param>
        /// <returns>物理的な駒の動かし方。</returns>
        public MoveByComputerResult MoveByComputer(string san)
        {
            MoveByComputerResult result = new MoveByComputerResult();
            string beforeFEN = this.GetFEN();
            bool ismovevalid;
            Dictionary<string, string> moveprogramresult = new Dictionary<string, string>();
            ShuchanPiecePosition fromPos;
            ShuchanPiecePosition toPos;
            PieceType promotion;
            string afterFEN, moveSAN;
            string tracecategory = "Computer's move";

            if (getMoveInfoFromHelpProgram(beforeFEN, san, out ismovevalid, moveprogramresult,
                out fromPos, out toPos, out promotion, out afterFEN, out moveSAN))
            {
                if (!ismovevalid)
                {
                    //コンピュータの指してがIllegalというのは整合性に問題が生じている。
                    Trace.WriteLine("Computer's move is illegal???", tracecategory);
                    result.TaskCompleted = false;
                }
                else
                {
                    Trace.WriteLine("Computer's move: " + moveSAN, tracecategory);
                    //移動タイプごとに結果を出力する。
                    #region Castling check
                    if (moveSAN.Contains("O-O-O"))
                    {
                        result.IsCastling = true;
                        if (WhiteOnMove)
                        {
                            //白のQueenside-castling
                            result.PhysicalMoves.Add(new MoveByComputerResult.PhysicalMoveItem("e1", "c1"));
                            result.PhysicalMoves.Add(new MoveByComputerResult.PhysicalMoveItem("a1", "d1"));
                            Trace.WriteLine("White Queenside castling", tracecategory);
                        }
                        else
                        {
                            //黒のQueenside-castling
                            result.PhysicalMoves.Add(new MoveByComputerResult.PhysicalMoveItem("e8", "c8"));
                            result.PhysicalMoves.Add(new MoveByComputerResult.PhysicalMoveItem("a8", "d8"));
                            Trace.WriteLine("Black Queenside castling", tracecategory);
                        }
                    }
                    else if (moveSAN.Contains("O-O"))
                    {
                        result.IsCastling = true;
                        if (WhiteOnMove)
                        {
                            //白のKingside-castling
                            result.PhysicalMoves.Add(new MoveByComputerResult.PhysicalMoveItem("e1", "g1"));
                            result.PhysicalMoves.Add(new MoveByComputerResult.PhysicalMoveItem("h1", "f1"));
                            Trace.WriteLine("White Kingside castling", tracecategory);
                        }
                        else
                        {
                            //黒のKingside-castling
                            result.PhysicalMoves.Add(new MoveByComputerResult.PhysicalMoveItem("e8", "g8"));
                            result.PhysicalMoves.Add(new MoveByComputerResult.PhysicalMoveItem("h8", "f8"));
                            Trace.WriteLine("Black Kingside castling", tracecategory);
                        }
                    }
                    #endregion
                    else
                    {
                        //移動先にすでに駒があればまず排除する。
                        if (board[toPos.Rank, toPos.File] != Piece.None)
                        {
                            //駒がある
                            Trace.WriteLine("A piece is already exist at the destination", tracecategory);
                            result.PhysicalMoves.Add(new MoveByComputerResult.PhysicalMoveItem(
                                toPos, new ShuchanPiecePosition(-1)));
                        }

                        //プロモーションがあれば既存のポーンを排除して新しい駒を用意する。
                        #region Promotion
                        if (promotion != PieceType.None)
                        {
                            //プロモーションあり
                            result.PromotedTo = promotion;
                            int nextextrapiece = extraPieceNextIndex(WhiteOnMove);
                            if (WhiteOnMove)
                            {
                                ExtraPieceWhite[nextextrapiece] = promotion;
                                //コンピュータは黒サイド固定なのでここには来ないはず
                                result.PhysicalMoves.Add(new MoveByComputerResult.PhysicalMoveItem(
                                    fromPos, new ShuchanPiecePosition(-1)));    //ポーンを捨てる
                                result.PhysicalMoves.Add(new MoveByComputerResult.PhysicalMoveItem(
                                    new ShuchanPiecePosition(ShuchanPiecePosition.ExtraPieceRank, nextextrapiece), toPos));   //予備駒を取り出す
                                Trace.WriteLine("Computer must be black side???", tracecategory);
                            }
                            else
                            {
                                ExtraPieceBlack[nextextrapiece] = promotion;
                                result.PhysicalMoves.Add(new MoveByComputerResult.PhysicalMoveItem(
                                    fromPos, new ShuchanPiecePosition(-1)));    //ポーンを捨てる
                                result.PhysicalMoves.Add(new MoveByComputerResult.PhysicalMoveItem(
                                    new ShuchanPiecePosition(ShuchanPiecePosition.ExtraPieceRank, nextextrapiece), toPos));   //予備駒を取り出す
                                Trace.WriteLine(string.Format("Promotion {0} occured", nextextrapiece), "Computer's move");
                            }
                        }
                        #endregion
                        else
                        {
                            //プロモーションなし
                            result.PhysicalMoves.Add(new MoveByComputerResult.PhysicalMoveItem(fromPos, toPos));
                            Trace.WriteLine("Normal move", "Computer's move");
                        }
                    }

                    result.TaskCompleted = true;
                    result.MoveSAN = moveSAN;
                    result.MoveFrom = fromPos;
                    result.MoveTo = toPos;
                    this.SetFen(afterFEN);
                }
            }
            else
            {
                //補助プログラムの実行に失敗。
                result.TaskCompleted = false;
            }

            return result;
        }

        /// <summary>
        /// 補助プログラムを使って、移動前FENと移動棋譜から移動に関する情報を得る。
        /// </summary>
        /// <param name="beforeFEN">移動前の盤のFEN。</param>
        /// <param name="move">移動を表す棋譜("e4", "O-O-O"など許容範囲は広い)。</param>
        /// <param name="valid">合法手ならtrueを返す。</param>
        /// <param name="result">補助プログラムからの応答の格納先。</param>
        /// <param name="fromPos">移動された駒の移動元を返す。</param>
        /// <param name="toPos">移動された駒の移動先を返す。</param>
        /// <param name="promotion">プロモーションが起こればそのプロモーション先。起こらなければPieceType.Noneを返す。</param>
        /// <param name="afterFEN">移動後の盤のFENを返す。</param>
        /// <param name="moveSAN">移動を表す棋譜をSAN形式で返す。</param>
        /// <returns>プログラムの呼び出しを正しく完了したらtrue。</returns>
        bool getMoveInfoFromHelpProgram(string beforeFEN, string move, out bool valid, Dictionary<string, string> result,
            out ShuchanPiecePosition fromPos, out ShuchanPiecePosition toPos, out PieceType promotion,
            out string afterFEN, out string moveSAN)
        {
            valid = false;
            fromPos = new ShuchanPiecePosition();
            toPos = new ShuchanPiecePosition();
            promotion = PieceType.None;
            afterFEN = "";
            moveSAN = "";

            //補助プログラムを実行して結果を得る。
            System.Diagnostics.Process helpprogram = new System.Diagnostics.Process();
            string stdout;
            string stderr;
            try
            {
                helpprogram.StartInfo.FileName = MoveComputingHelperExe;
                helpprogram.StartInfo.Arguments = MoveComputingHelperArgument + " " + beforeFEN.Replace(' ', '_') + " " + move;
                helpprogram.StartInfo.ErrorDialog = false;
                helpprogram.StartInfo.CreateNoWindow = true;
                helpprogram.StartInfo.RedirectStandardError = true;
                helpprogram.StartInfo.RedirectStandardOutput = true;
                helpprogram.StartInfo.UseShellExecute = false;
                helpprogram.Start();
                stdout = helpprogram.StandardOutput.ReadToEnd();
                stderr = helpprogram.StandardError.ReadToEnd();
                helpprogram.WaitForExit(10000);
                helpprogram.Dispose();
            }
            catch (Exception ex)
            {
                Trace.WriteLine(ex, "getMoveInfoFromHelpProgram()");
                return false;
            }

            System.IO.StringReader sr = new System.IO.StringReader(stdout);
            while (sr.Peek() > 0)
            {
                string line = sr.ReadLine();
                string[] parts = line.Split(new char[] { ':' }, StringSplitOptions.RemoveEmptyEntries);
                if (parts.Length != 2)
                {
                    continue;
                }

                result.Add(parts[0], parts[1]);
            }

            if (result.ContainsKey("Move"))
            {
                if (result["Move"] == "Valid")
                {
                    //この手は合法手。
                    valid = true;
                    try
                    {
                        fromPos = new ShuchanPiecePosition(result["MoveFrom"]);
                        toPos = new ShuchanPiecePosition(result["MoveTo"]);
                        if (result.ContainsKey("Promotion"))
                        {
                            switch (result["Promotion"].ToLower())
                            {
                                case "q":
                                    promotion = PieceType.Queen;
                                    break;
                                case "n":
                                    promotion = PieceType.Knight;
                                    break;
                                case "b":
                                    promotion = PieceType.Bishop;
                                    break;
                                case "r":
                                    promotion = PieceType.Rook;
                                    break;
                                default:
                                    break;
                            }
                        }
                        afterFEN = result["AfterFEN"];
                        moveSAN = result["MoveSAN"];
                    }
                    catch (Exception ex)
                    {
                        Trace.WriteLine(ex, "getMoveInfoFromHelpProgram()");
                        return false;
                    }
                }
            }

            return true;
        }

        #region 追加駒関数
        /// <summary>
        /// 追加駒の文字(A-H, a-h)を、ExtraPiece*のインデックス(0-7)に変換する。
        /// </summary>
        /// <param name="c"></param>
        /// <returns></returns>
        int char2ExtraPieceIndex(char c)
        {
            c = char.ToLower(c);
            int result = c - 'a';
            return result;
        }

        /// <summary>
        /// ExtraPiece*のインデックス(0-7)を、追加駒の文字(A-H, a-h)に変換する。
        /// </summary>
        /// <param name="index"></param>
        /// <param name="white">白サイドの追加駒についての問い合わせならtrue。</param>
        /// <returns></returns>
        char extraPieceIndex2Char(int index, bool white)
        {
            char result = (char)('a' + index);
            if (white)
            {
                result = char.ToUpper(result);
            }

            return result;
        }

        /// <summary>
        /// 次に使うべき(まだ使われていない)追加駒のインデックス。
        /// </summary>
        /// <param name="white">白サイドの追加駒についての問い合わせならtrue。</param>
        /// <returns></returns>
        int extraPieceNextIndex(bool white)
        {
            for (int i = 0; i < NExtraPiece; i++)
            {
                if (white)
                {
                    if (ExtraPieceWhite[i] == PieceType.None)
                    {
                        //このインデックスはまだ使われていない。
                        return i;
                    }
                }
                else
                {
                    if (ExtraPieceBlack[i] == PieceType.None)
                    {
                        //このインデックスはまだ使われていない。
                        return i;
                    }
                }
            }

            return NExtraPiece; //追加駒は尽きている。
        }
        #endregion

        public override string ToString()
        {
            return GetFEN();
        }
    }
}
