C#で将棋盤を描画してみようその2.2 ～駒の描画2～

[その2.1](http://qiita.com/pizyumi/items/e1605bd6ca8284a38070)からの続き。

# その6 描画資源

駒を描画するためには駒の画像が必要です。そのような、描画を行うために必要な資源はプログラムの実行直後に読み込むことになりますが（更に、実行中に設定を変更できるような作りになっているプログラムの場合には、設定の変更直後に資源の再読み込みが必要になるような場合も考えられるでしょう）、そのような資源を保持するためのクラスを作成しましょう。

画像は存在しない場合も考えられるので随意型となります。

```csharp
    //描画資源
    public class Resource
    {
        //先手の歩兵の画像
        public Option<Image> Hu { get; set; }
        //先手の銀将の画像
        public Option<Image> Gin { get; set; }
        //先手の飛車の画像
        public Option<Image> Hi { get; set; }
        //先手の角行の画像
        public Option<Image> Kaku { get; set; }
        //先手の桂馬の画像
        public Option<Image> Kei { get; set; }
        //先手の金将の画像
        public Option<Image> Kin { get; set; }
        //先手の香車の画像
        public Option<Image> Kyou { get; set; }
        //先手の王将の画像
        public Option<Image> Ou { get; set; }
        //先手のと金の画像
        public Option<Image> HuPromoted { get; set; }
        //先手の成銀の画像
        public Option<Image> GinPromoted { get; set; }
        //先手の竜王の画像
        public Option<Image> HiPromoted { get; set; }
        //先手の竜馬の画像
        public Option<Image> KakuPromoted { get; set; }
        //先手の成桂の画像
        public Option<Image> KeiPromoted { get; set; }
        //先手の成香の画像
        public Option<Image> KyouPromoted { get; set; }
        //後手の歩兵の画像
        public Option<Image> HuInverted { get; set; }
        //後手の銀将の画像
        public Option<Image> GinInverted { get; set; }
        //後手の飛車の画像
        public Option<Image> HiInverted { get; set; }
        //後手の角行の画像
        public Option<Image> KakuInverted { get; set; }
        //後手の桂馬の画像
        public Option<Image> KeiInverted { get; set; }
        //後手の金将の画像
        public Option<Image> KinInverted { get; set; }
        //後手の香車の画像
        public Option<Image> KyouInverted { get; set; }
        //後手の王将の画像
        public Option<Image> OuInverted { get; set; }
        //後手のと金の画像
        public Option<Image> HuPromotedInverted { get; set; }
        //後手の成銀の画像
        public Option<Image> GinPromotedInverted { get; set; }
        //後手の竜王の画像
        public Option<Image> HiPromotedInverted { get; set; }
        //後手の竜馬の画像
        public Option<Image> KakuPromotedInverted { get; set; }
        //後手の成桂の画像
        public Option<Image> KeiPromotedInverted { get; set; }
        //後手の成香の画像
        public Option<Image> KyouPromotedInverted { get; set; }
    }
```

# その7 描画資源に関する処理

描画資源に関する処理をSyougiクラスに追加していきます。

まずは描画資源の読み込みですが、その前にファイルに格納されているデータを読み込むための関数とデータから画像を作成するための関数をProgramクラスに追加しましょう。

```csharp
        //指定された場所に存在するファイルの内容を読み込み、返す
        //フォルダが存在しない場合やファイルが存在しない場合や存在するが読み込みに失敗した場合にはNoneを返す
        public static Option<byte[]> LoadFile(string path)
        {
            try
            {
                //指定された場所に存在するファイルの内容を読み込み、返す
                return Option.Return(File.ReadAllBytes(path));
            }
            //フォルダが存在しない場合
            catch(DirectoryNotFoundException)
            {
                //過誤の運用記録を出力する
                const string id = "e28fd67d-7895-457a-a84e-9570d96c6c84";
                const string text = "not found folder";
                const LogType type = LogType.Error;

                Logger.IssueLog(new LogItem(Guid.Parse(id), text, type, FormalName));

                //Noneを返す
                return Option.Return<byte[]>();
            }
            //ファイルが存在しない場合
            catch (FileNotFoundException)
            {
                //過誤の運用記録を出力する
                const string id = "63c1dd58-45f2-4b97-935f-956bcda81a7a";
                const string text = "not found file";
                const LogType type = LogType.Error;

                Logger.IssueLog(new LogItem(Guid.Parse(id), text, type, FormalName));

                //Noneを返す
                return Option.Return<byte[]>();
            }
            //ファイルが存在するが読み込みに失敗した場合
            catch (Exception ex)
            {
                //過誤の運用記録を出力する（例外の情報を付随させる）
                const string id = "4d4b2a03-8521-492d-97b6-895367b2cf29";
                const string text = "unexpected error";
                const LogType type = LogType.Error;

                Logger.IssueLog(new LogItem(Guid.Parse(id), text, type, FormalName, new Info() { { "ex", ex } }));

                //Noneを返す
                return Option.Return<byte[]>();
            }
        }

        //指定された内容から画像を作成し、返す
        //内容が画像として認識できない場合やそうではないが作成に失敗した場合にはNoneを返す
        public static Option<Image> CreateImage(byte[] data)
        {
            try
            {
                //指定された内容から画像を作成し、返す
                using (MemoryStream ms = new MemoryStream(data))
                    return Option.Return(Image.FromStream(ms));
            }
            //内容が画像として認識できない場合
            catch (ArgumentException)
            {
                //過誤の運用記録を出力する
                const string id = "d08298fb-0715-4346-a7e5-8ffa4814732c";
                const string text = "bad format image";
                const LogType type = LogType.Error;

                Logger.IssueLog(new LogItem(Guid.Parse(id), text, type, FormalName));

                //Noneを返す
                return Option.Return<Image>();
            }
            //内容が画像として認識できるが作成に失敗した場合
            catch (Exception ex)
            {
                //過誤の運用記録を出力する（例外の情報を付随させる）
                const string id = "68bffe2f-ff14-4dc1-8fa6-fca134eeec86";
                const string text = "unexpected error";
                const LogType type = LogType.Error;

                Logger.IssueLog(new LogItem(Guid.Parse(id), text, type, FormalName, new Info() { { "ex", ex } }));

                //Noneを返す
                return Option.Return<Image>();
            }
        }
    }
```

更に拡張メソッドを追加します。

```csharp
    //共通の拡張関数
    public static class CommonExtension
    {
        //関数合成（通常の関数合成の逆順）
        public static Func<T, R> AndThen<T, S, R>(this Func<T, S> f, Func<S, R> g)
        {
            return a => g(f(a));
        }
    }
```

これらを使って描画資源の読み込みを行う関数を追加します。読み込みを行うファイルが存在するフォルダの場所を引数として受け取ります。駒の画像ファイルの拡張子は`.png`とします。また、駒の画像ファイルの名称を一意に定めます。たとえば、後手のと金の画像ファイルの名称は`huProInv.png`となります。

```csharp
        //ファイルが存在するフォルダの場所を受け取って描画資源を作成する
        private static Resource CreateResource(string folder)
        {
            //拡張子
            const string extension = ".png";

            //歩兵の画像のファイル名の主体
            const string hu = "hu";
            //銀将の画像のファイル名の主体
            const string gin = "gin";
            //飛車の画像のファイル名の主体
            const string hi = "hi";
            //角行の画像のファイル名の主体
            const string kaku = "kaku";
            //桂馬の画像のファイル名の主体
            const string kei = "kei";
            //金将の画像のファイル名の主体
            const string kin = "kin";
            //香車の画像のファイル名の主体
            const string kyou = "kyou";
            //王将の画像のファイル名の主体
            const string ou = "ou";

            //後手の駒である場合の接尾辞
            const string inverted = "Inv";
            //成駒である場合の接尾辞
            const string promoted = "Pro";

            //成駒の画像のファイル名の主体を作成する
            Func<string, string> createNamePromoted = (piece) => piece + promoted;
            //後手の駒の画像のファイル名の主体を作成する
            Func<string, string> createNameInverted = (piece) => piece + inverted;
            //画像のファイルの場所を作成する
            Func<string, string> createPath = (piece) => Path.Combine(folder, piece + extension);

            //画像のファイルの内容から画像を作成する
            Func<string, Option<Image>> loadImage = createPath.AndThen(Program.LoadFile).AndThen(Program.CreateImage);
            Func<string, Option<Image>> loadImagePromoted = createNamePromoted.AndThen(loadImage);
            Func<string, Option<Image>> loadImageInverted = createNameInverted.AndThen(loadImage);
            Func<string, Option<Image>> loadImagePromotedInverted = createNamePromoted.AndThen(createNameInverted).AndThen(loadImage);

            //描画資源を作成する
            Resource resource = new Resource();

            //描画資源を設定する
            resource.Hu = loadImage(hu);
            resource.Gin = loadImage(gin);
            resource.Hi = loadImage(hi);
            resource.Kaku = loadImage(kaku);
            resource.Kei = loadImage(kei);
            resource.Kin = loadImage(kin);
            resource.Kyou = loadImage(kyou);
            resource.Ou = loadImage(ou);
            resource.HuPromoted = loadImagePromoted(hu);
            resource.GinPromoted = loadImagePromoted(gin);
            resource.HiPromoted = loadImagePromoted(hi);
            resource.KakuPromoted = loadImagePromoted(kaku);
            resource.KeiPromoted = loadImagePromoted(kei);
            resource.KyouPromoted = loadImagePromoted(kyou);
            resource.HuInverted = loadImageInverted(hu);
            resource.GinInverted = loadImageInverted(gin);
            resource.HiInverted = loadImageInverted(hi);
            resource.KakuInverted = loadImageInverted(kaku);
            resource.KeiInverted = loadImageInverted(kei);
            resource.KinInverted = loadImageInverted(kin);
            resource.KyouInverted = loadImageInverted(kyou);
            resource.OuInverted = loadImageInverted(ou);
            resource.HuPromotedInverted = loadImagePromotedInverted(hu);
            resource.GinPromotedInverted = loadImagePromotedInverted(gin);
            resource.HiPromotedInverted = loadImagePromotedInverted(hi);
            resource.KakuPromotedInverted = loadImagePromotedInverted(kaku);
            resource.KeiPromotedInverted = loadImagePromotedInverted(kei);
            resource.KyouPromotedInverted = loadImagePromotedInverted(kyou);

            //描画資源を返す
            return resource;
        }
```

描画資源と駒を引数として受け取って駒に対応する駒の画像を返す関数を追加します。画像が存在しない場合にはNoneを返すことにします。描画資源のプロパティ自体が随意型なのでプロパティの値をそのまま返すだけです。駒としてあり得ないものが引数として渡された場合にもNoneを返すことになります。

```csharp
        //描画資源と駒を受け取って駒に対応する駒の画像を返す（画像が存在しない場合にはNoneを返す）
        private static Option<Image> GetImage(Resource resource, Piece piece)
        {
            //駒の所有者が先手の場合
            if (piece.Owner == Owner.Sente)
            {
                //駒が歩兵の場合
                if (piece is Hu)
                    //駒が成り駒の場合
                    if (!(piece as Hu).IsPromoted)
                        return resource.Hu;
                    //駒が成り駒でない場合
                    else
                        return resource.HuPromoted;
                //駒が銀将の場合
                else if (piece is Gin)
                    //駒が成り駒の場合
                    if (!(piece as Gin).IsPromoted)
                        return resource.Gin;
                    //駒が成り駒でない場合
                    else
                        return resource.GinPromoted;
                //駒が飛車の場合
                else if (piece is Hi)
                    //駒が成り駒の場合
                    if (!(piece as Hi).IsPromoted)
                        return resource.Hi;
                    //駒が成り駒でない場合
                    else
                        return resource.HiPromoted;
                //駒が角行の場合
                else if (piece is Kaku)
                    //駒が成り駒の場合
                    if (!(piece as Kaku).IsPromoted)
                        return resource.Kaku;
                    //駒が成り駒でない場合
                    else
                        return resource.KakuPromoted;
                //駒が桂馬の場合
                else if (piece is Kei)
                    //駒が成り駒の場合
                    if (!(piece as Kei).IsPromoted)
                        return resource.Kei;
                    //駒が成り駒でない場合
                    else
                        return resource.KeiPromoted;
                //駒が金将の場合
                else if (piece is Kin)
                    return resource.Kin;
                //駒が香車の場合
                else if (piece is Kyou)
                    //駒が成り駒の場合
                    if (!(piece as Kyou).IsPromoted)
                        return resource.Kyou;
                    //駒が成り駒でない場合
                    else
                        return resource.KyouPromoted;
                //駒が王将の場合
                else if (piece is Ou)
                    return resource.Ou;
                //駒が存在し得ない駒の場合
                else
                    return new None<Image>();
            }
            //駒の所有者が後手の場合
            else if (piece.Owner == Owner.Gote)
            {
                //駒が歩兵の場合
                if (piece is Hu)
                    //駒が成り駒の場合
                    if (!(piece as Hu).IsPromoted)
                        return resource.HuInverted;
                    //駒が成り駒でない場合
                    else
                        return resource.HuPromotedInverted;
                //駒が銀将の場合
                else if (piece is Gin)
                    //駒が成り駒の場合
                    if (!(piece as Gin).IsPromoted)
                        return resource.GinInverted;
                    //駒が成り駒でない場合
                    else
                        return resource.GinPromotedInverted;
                //駒が飛車の場合
                else if (piece is Hi)
                    //駒が成り駒の場合
                    if (!(piece as Hi).IsPromoted)
                        return resource.HiInverted;
                    //駒が成り駒でない場合
                    else
                        return resource.HiPromotedInverted;
                //駒が角行の場合
                else if (piece is Kaku)
                    //駒が成り駒の場合
                    if (!(piece as Kaku).IsPromoted)
                        return resource.KakuInverted;
                    //駒が成り駒でない場合
                    else
                        return resource.KakuPromotedInverted;
                //駒が桂馬の場合
                else if (piece is Kei)
                    //駒が成り駒の場合
                    if (!(piece as Kei).IsPromoted)
                        return resource.KeiInverted;
                    //駒が成り駒でない場合
                    else
                        return resource.KeiPromotedInverted;
                //駒が金将の場合
                else if (piece is Kin)
                    return resource.KinInverted;
                //駒が香車の場合
                else if (piece is Kyou)
                    //駒が成り駒の場合
                    if (!(piece as Kyou).IsPromoted)
                        return resource.KyouInverted;
                    //駒が成り駒でない場合
                    else
                        return resource.KyouPromotedInverted;
                //駒が王将の場合
                else if (piece is Ou)
                    return resource.OuInverted;
                //駒が存在し得ない駒の場合
                else
                    return new None<Image>();
            }
            //駒の所有者が存在し得ない所有者の場合
            else
                return new None<Image>();
        }
```

描画資源として作成した画像は資源なので不必要になったら処分しなければなりません。描画資源を引数として受け取って描画資源の処分を行う関数を追加します。

```csharp
        //描画資源を受け取って描画資源の処分を行う
        private static void DisposeResource(Resource resource)
        {
            //描画資源のそれぞれの画像を処分する
            resource.Hu.Match(some: (image) => image.Dispose());
            resource.Gin.Match(some: (image) => image.Dispose());
            resource.Hi.Match(some: (image) => image.Dispose());
            resource.Kaku.Match(some: (image) => image.Dispose());
            resource.Kei.Match(some: (image) => image.Dispose());
            resource.Kin.Match(some: (image) => image.Dispose());
            resource.Kyou.Match(some: (image) => image.Dispose());
            resource.Ou.Match(some: (image) => image.Dispose());
            resource.HuPromoted.Match(some: (image) => image.Dispose());
            resource.GinPromoted.Match(some: (image) => image.Dispose());
            resource.HiPromoted.Match(some: (image) => image.Dispose());
            resource.KakuPromoted.Match(some: (image) => image.Dispose());
            resource.KeiPromoted.Match(some: (image) => image.Dispose());
            resource.KyouPromoted.Match(some: (image) => image.Dispose());
            resource.HuInverted.Match(some: (image) => image.Dispose());
            resource.GinInverted.Match(some: (image) => image.Dispose());
            resource.HiInverted.Match(some: (image) => image.Dispose());
            resource.KakuInverted.Match(some: (image) => image.Dispose());
            resource.KeiInverted.Match(some: (image) => image.Dispose());
            resource.KinInverted.Match(some: (image) => image.Dispose());
            resource.KyouInverted.Match(some: (image) => image.Dispose());
            resource.OuInverted.Match(some: (image) => image.Dispose());
            resource.HuPromotedInverted.Match(some: (image) => image.Dispose());
            resource.GinPromotedInverted.Match(some: (image) => image.Dispose());
            resource.HiPromotedInverted.Match(some: (image) => image.Dispose());
            resource.KakuPromotedInverted.Match(some: (image) => image.Dispose());
            resource.KeiPromotedInverted.Match(some: (image) => image.Dispose());
            resource.KyouPromotedInverted.Match(some: (image) => image.Dispose());
        }
```

Syougiクラスは資源を保持するクラスということになるので、IDisposableインターフェイスを実装しなければなりません。実装しましょう。

```csharp
    //将棋に関する処理
    //資源を保持するためIDisposableインターフェイスを実装しなければならない
    public class Syougi : IDisposable
    {
        //資源の処分が既に行われているか
        private bool _disposed;

        //資源を処分する（子クラスで使用するためのもの）
        protected virtual void Dispose(bool disposing)
        {
            //資源の処分が既に行われている場合には何もしない
            if (_disposed)
                return;

            //資源の処分が必要である場合には描画資源を処分する
            if (disposing)
                DisposeResource(Resource);

            //資源の処分が既に行われた
            _disposed = true;
        }

        //資源を処分する（外部から使用するためのもの）
        public void Dispose()
        {
            //資源を処分する処理を呼び出す
            Dispose(true);
            //このクラスの終了処理を呼び出さないように設定する
            GC.SuppressFinalize(this);
        }

        //終了処理
        ~Syougi()
        {
            //資源を処分する処理を呼び出す
            Dispose(false);
        }
    }
```