トップページに戻る    次のC#のサンプルへ    前のC#のサンプルへ

24-40 へびいちご

問題

ニコリのへびいちごを解きます。

例題と途中経過と答え
        

1 盤面の白マスに、
  ヘビ(1から5までの数字が1つずつ順にタテヨコにつながったピース)をいくつか配置しましょう。

2 黒マスの中の数字は、そのマスから矢印の方向に眺めたとき、
  いちばん近くに見える数字を表します。
  0の場合は、そのマスから黒マスか外周に至るまでにヘビがいないことを表します。

3 ヘビは1が頭、5が尾となります。
  2→1とつながった方向(すなわちヘビの視線の先)に、黒マスか外周に至るまで、
  他のヘビの一部もしくは全部が入るようにしてはいけません。攻撃しちゃいますからね。

4 異なるヘビどうしが辺を共有するようにしてはいけません。なわばり争いが起こるからです。


ソース

using System;
using System.Collections.Generic;
using System.Linq;

class Program
{
    struct PointDef
    {
        internal int X;
        internal int Y;
    }

    //矢印情報の構造体
    struct ArrowInfoDef
    {
        internal PointDef Point;
        internal char NumVal;
        internal PointDef[] PointArr;
    }

    //深さ優先探索の状態Def
    struct JyoutaiDef
    {
        internal char[,] BanArr;
        internal char[,] HebiIDArr; //半角空白と、1から9とAからZの36進数でヘビIDを管理
        internal int CurrX;
        internal int CurrY;
    }

    static char[,] QuestionArrowArr;
    static char[,] QuestionBanNumArr;
    static int UB_X;
    static int UB_Y;

    static ArrowInfoDef[] mArrowInfoArr; //矢印情報の配列

    static System.Diagnostics.Stopwatch sw = System.Diagnostics.Stopwatch.StartNew();

    static void Main()
    {
        char[,] Q01_ArrowArr = {{'□','□','□','□','□'},
                                {'■','□','□','→','□'},
                                {'□','□','↑','□','□'},
                                {'□','↓','□','□','↑'},
                                {'□','□','□','□','□'}};

        char[,] Q01_BanNumArr = {{' ',' ' ,' ' ,' ' ,' '},
                                 {' ',' ' ,' ' ,'3' ,' '},
                                 {' ',' ' ,'5' ,' ' ,' '},
                                 {' ','0' ,' ' ,' ' ,'4'},
                                 {' ',' ' ,' ' ,' ' ,' '}};

        QuestionArrowArr = XYRev(Q01_ArrowArr);
        QuestionBanNumArr = XYRev(Q01_BanNumArr);

        UB_X = QuestionArrowArr.GetUpperBound(0);
        UB_Y = QuestionArrowArr.GetUpperBound(1);

        //第1フェーズ 矢印情報を作成する
        CreateArrowInfoArr();

        //デバッグ用の矢印情報の出力
        //DebugPrintArrowInfo();

        char[,] BanArr = new char[UB_X + 1, UB_Y + 1];
        char[,] HebiIDArr = new char[UB_X + 1, UB_Y + 1];

        for (int X = 0; X <= UB_X; X++) {
            for (int Y = 0; Y <= UB_Y; Y++) {
                char CurrMasu = QuestionArrowArr[X, Y];
                BanArr[X, Y] = (CurrMasu == '□' ? ' ' : '*');
                HebiIDArr[X, Y] = ' ';
            }
        }

        //第2フェーズ 初回の確定探索
        SyokaiKakuteiTansaku(BanArr);

        //第3フェーズ 確定探索
        ExecKakuteiTansaku(BanArr, HebiIDArr);

        //第4フェーズ 確定探索付の深さ優先探索
        ExecDFS(BanArr, HebiIDArr);
    }

    //X座標とY座標の入れ替え
    static char[,] XYRev(char[,] pBaseArr)
    {
        int RevArrUB_X = pBaseArr.GetUpperBound(1);
        int RevArrUB_Y = pBaseArr.GetUpperBound(0);
        char[,] WillReturnArr = new char[RevArrUB_X + 1, RevArrUB_Y + 1];
        for (int X = 0; X <= RevArrUB_X; X++) {
            for (int Y = 0; Y <= RevArrUB_Y; Y++) {
                WillReturnArr[X, Y] = pBaseArr[Y, X];
            }
        }
        return WillReturnArr;
    }

    //第1フェーズ 矢印情報を作成する
    static void CreateArrowInfoArr()
    {
        var ArrowInfoList = new List<ArrowInfoDef>();

        for (int LoopX = 0; LoopX <= UB_X; LoopX++) {
            for (int LoopY = 0; LoopY <= UB_Y; LoopY++) {
                char CurrMasu = QuestionArrowArr[LoopX, LoopY];
                if (CurrMasu == '↑' || CurrMasu == '→'
                 || CurrMasu == '↓' || CurrMasu == '←') {
                    char CurrNumVal = QuestionBanNumArr[LoopX, LoopY];
                    ArrowInfoDef WillAdd;
                    WillAdd = DeriveArrowInfoArrHelper(CurrNumVal, LoopX, LoopY);
                    ArrowInfoList.Add(WillAdd);
                }
            }
        }
        mArrowInfoArr = ArrowInfoList.ToArray();
    }

    //第1フェーズ 矢印情報を作成する
    static ArrowInfoDef DeriveArrowInfoArrHelper(char pNumVal, int pX, int pY)
    {
        ArrowInfoDef WillReturn;
        WillReturn.Point = new PointDef() { X = pX, Y = pY };
        WillReturn.NumVal = pNumVal;

        char CurrMasu = QuestionArrowArr[pX, pY];

        //変位ベクトルの値
        int Heni_X = 0, Heni_Y = 0;
        if (CurrMasu == '↑') Heni_Y = -1;
        if (CurrMasu == '→') Heni_X = 1;
        if (CurrMasu == '↓') Heni_Y = 1;
        if (CurrMasu == '←') Heni_X = -1;

        var PointList = new List<PointDef>();

        int LoopX = pX, LoopY = pY;
        while (true) {
            LoopX += Heni_X; LoopY += Heni_Y;
            if (LoopX < 0 || UB_X < LoopX) break;
            if (LoopY < 0 || UB_Y < LoopY) break;

            CurrMasu = QuestionArrowArr[LoopX, LoopY];

            //空白でなかったらBreak
            if (CurrMasu != '□') break;

            PointList.Add(new PointDef() { X = LoopX, Y = LoopY });
        }
        WillReturn.PointArr = PointList.ToArray();
        return WillReturn;
    }

    //デバッグ用の矢印情報の出力
    static void DebugPrintArrowInfo()
    {
        foreach (var EachArrowInfo in mArrowInfoArr) {
            Console.WriteLine("■■■■■■■■■■■■■");
            Console.WriteLine("NumVal={0}", EachArrowInfo.NumVal);
            Console.WriteLine("Point=({0},{1})", EachArrowInfo.Point.X, EachArrowInfo.Point.Y);

            for (int I = 0; I <= EachArrowInfo.PointArr.GetUpperBound(0); I++) {
                Console.WriteLine("PointArr[{0}]=({1},{2})", I,
                    EachArrowInfo.PointArr[I].X, EachArrowInfo.PointArr[I].Y);
            }
        }
    }

    //第2フェーズ 初回の確定探索
    static void SyokaiKakuteiTansaku(char[,] pBanArr)
    {
        //確定探索1 0の矢印の先を確定空白に設定
        KakuteiTansaku1(pBanArr);
    }

    //確定探索1 0の矢印の先を確定空白に設定
    static void KakuteiTansaku1(char[,] pBanArr)
    {
        ArrowInfoDef[] wkArrowInfoArr = Array.FindAll(mArrowInfoArr, A => A.NumVal == '0');
        foreach (ArrowInfoDef EachArrowInfo in wkArrowInfoArr) {
            foreach (PointDef EachPoint in EachArrowInfo.PointArr) {
                pBanArr[EachPoint.X, EachPoint.Y] = '/';
            }
        }
    }

    //第3フェーズ 確定探索を行い有効な盤面かを返す
    static bool ExecKakuteiTansaku(char[,] pBanArr, char[,] pHebiIDArr)
    {
        while (true) {
            char[,] PrevBanArr = (char[,])pBanArr.Clone();

            //確定探索2 矢印の数値の埋める候補が1つしかなければ確定
            if (KakuteiTansaku2(pBanArr) == false) return false;

            //確定探索3 数値マスに隣接した空白マスが1マスの場合
            if (KakuteiTansaku3(pBanArr, pHebiIDArr) == false) return false;

            //確定探索4 矢印の数値マスの1つ前を確定空白に設定
            KakuteiTansaku4(pBanArr);

            //確定探索で確定するマスがなくなったらBreak
            if (pBanArr.Cast<char>().SequenceEqual(PrevBanArr.Cast<char>())) break;
        }
        return true;
    }

    //確定探索2 矢印の数値の埋める候補が1つしかなければ確定
    //戻り値として、有効な盤面かを返す
    static bool KakuteiTansaku2(char[,] pBanArr)
    {
        foreach (ArrowInfoDef EachArrowInfo in mArrowInfoArr) {
            //埋める値
            char FillNum = EachArrowInfo.NumVal;

            //数値が0なら対象外
            if (FillNum == '0') continue;

            //既に矢印の数値が埋められていたら対象外
            int wkInd = Array.FindIndex(EachArrowInfo.PointArr, A => pBanArr[A.X, A.Y] != ' '
                                                                  && pBanArr[A.X, A.Y] != '/');
            if (wkInd >= 0) {
                if (pBanArr[EachArrowInfo.PointArr[wkInd].X,
                            EachArrowInfo.PointArr[wkInd].Y] == FillNum) {
                    continue;
                }
            }

            //矢印の数値を埋める候補の座標の配列を返す
            PointDef[] PointKouhoArr = DeriveFillNumPointKouhoArr(pBanArr, EachArrowInfo);

            //候補座標数が1なら確定
            if (PointKouhoArr.Length == 1) {
                pBanArr[PointKouhoArr[0].X, PointKouhoArr[0].Y] = FillNum;
                if (IsValid(pBanArr, PointKouhoArr[0].X, PointKouhoArr[0].Y) == false)
                    return false;
            }
        }
        return true;
    }

    //矢印の数値を埋める候補の座標の配列を返す
    static PointDef[] DeriveFillNumPointKouhoArr(char[,] pBanArr, ArrowInfoDef pArrowInfo)
    {
        var WillReturnList = new List<PointDef>(pArrowInfo.PointArr);

        //既に埋まっている数値に遭遇するまでが候補座標
        WillReturnList = WillReturnList.TakeWhile(A => pBanArr[A.X, A.Y] == ' '
                                                    || pBanArr[A.X, A.Y] == '/').ToList();

        //確定空白は除外
        WillReturnList.RemoveAll(A => pBanArr[A.X, A.Y] == '/');

        //他の数値の第一候補なら除外
        ArrowInfoDef[] wkArrowInfoArr
            = Array.FindAll(mArrowInfoArr, A => A.NumVal != 0
                                             && A.NumVal != pArrowInfo.NumVal);
        foreach (ArrowInfoDef EachArrowInfo in wkArrowInfoArr) {
            IEnumerable<PointDef> wkEnum =
                EachArrowInfo.PointArr.SkipWhile(A => pBanArr[A.X, A.Y] == '/').Take(1);
            if (wkEnum.Any() == false) continue;
            WillReturnList.RemoveAll(A => A.X == wkEnum.First().X
                                       && A.Y == wkEnum.First().Y);
        }

        return WillReturnList.ToArray();
    }

    //チェック対象座標を引数として、有効な盤面かを判定
    static bool IsValid(char[,] pBanArr, int pX, int pY)
    {
        //カレントマスが数字マスでなかったら、チェックしない
        char CurrMasu = pBanArr[pX, pY];
        if (CurrMasu < '1' || '5' < CurrMasu) return true;

        //指定マスに隣接した盤面の値
        char[] wkRinsetuArr = DeriveRinsetuMasuArr(pBanArr, pX, pY);

        //同じ数字は隣接NG
        if (wkRinsetuArr.Contains(CurrMasu)) return false;

        //隣接NGのチェック
        if (CurrMasu == '1' && wkRinsetuArr.Contains('3')) return false;
        if (CurrMasu == '1' && wkRinsetuArr.Contains('5')) return false;
        if (CurrMasu == '2' && wkRinsetuArr.Contains('4')) return false;
        if (CurrMasu == '3' && wkRinsetuArr.Contains('1')) return false;
        if (CurrMasu == '3' && wkRinsetuArr.Contains('5')) return false;
        if (CurrMasu == '4' && wkRinsetuArr.Contains('2')) return false;
        if (CurrMasu == '5' && wkRinsetuArr.Contains('1')) return false;
        if (CurrMasu == '5' && wkRinsetuArr.Contains('3')) return false;

        //同じ数字が2つ以上隣接したらNG
        for (char LoopI = '1'; LoopI <= '5'; LoopI++)
            if (wkRinsetuArr.Count(A => A == LoopI) >= 2) return false;

        //必要な数字と隣接してなくて、隣接空白が不足していたらNG
        int NeedSpace = 0;
        if (CurrMasu == '1' && wkRinsetuArr.Contains('2') == false) NeedSpace++;
        if (CurrMasu == '2' && wkRinsetuArr.Contains('1') == false) NeedSpace++;
        if (CurrMasu == '2' && wkRinsetuArr.Contains('3') == false) NeedSpace++;
        if (CurrMasu == '3' && wkRinsetuArr.Contains('2') == false) NeedSpace++;
        if (CurrMasu == '3' && wkRinsetuArr.Contains('4') == false) NeedSpace++;
        if (CurrMasu == '4' && wkRinsetuArr.Contains('3') == false) NeedSpace++;
        if (CurrMasu == '4' && wkRinsetuArr.Contains('5') == false) NeedSpace++;
        if (CurrMasu == '5' && wkRinsetuArr.Contains('4') == false) NeedSpace++;
        if (wkRinsetuArr.Count(A => A == ' ') < NeedSpace) return false;

        //矢印の数値を満たせなくなったらNG
        foreach (ArrowInfoDef EachArrowInfo in mArrowInfoArr) {
            //0と埋めた数値はチェック対象外
            if (EachArrowInfo.NumVal == '0'
             || EachArrowInfo.NumVal == pBanArr[pX, pY]) continue;

            //数値を設定した座標を含んでなければチェック対象外
            if (Array.Exists(EachArrowInfo.PointArr, A => A.X == pX && A.Y == pY) == false)
                continue;

            IEnumerable<PointDef> wkEnum =
                EachArrowInfo.PointArr.TakeWhile(A => pBanArr[A.X, A.Y] == ' '
                                                   || pBanArr[A.X, A.Y] == '/'
                                                   || pBanArr[A.X, A.Y] == EachArrowInfo.NumVal);
            //確定空白以外が0件ならNG
            if (wkEnum.Any(A => pBanArr[A.X, A.Y] != '/') == false) return false;
        }

        return true;
    }

    //指定マスに隣接した盤面の値を配列で返す
    static char[] DeriveRinsetuMasuArr(char[,] pBanArr, int pBaseX, int pBaseY)
    {
        var WillReturnList = new List<char>();

        if (pBaseX > 0)
            WillReturnList.Add(pBanArr[pBaseX - 1, pBaseY]);
        if (pBaseX < UB_X)
            WillReturnList.Add(pBanArr[pBaseX + 1, pBaseY]);
        if (pBaseY > 0)
            WillReturnList.Add(pBanArr[pBaseX, pBaseY - 1]);
        if (pBaseY < UB_Y)
            WillReturnList.Add(pBanArr[pBaseX, pBaseY + 1]);

        return WillReturnList.ToArray();
    }

    //指定マスに隣接した空白の座標を配列で返す
    static PointDef[] DeriveRinsetuSpacePointArr(char[,] pBanArr, int pBaseX, int pBaseY)
    {
        var WillReturnList = new List<PointDef>();

        Action<int, int> AddAct = (pX, pY) =>
        {
            if (pBanArr[pX, pY] != ' ') return;
            WillReturnList.Add(new PointDef() { X = pX, Y = pY });
        };

        if (pBaseX > 0) AddAct(pBaseX - 1, pBaseY);
        if (pBaseX < UB_X) AddAct(pBaseX + 1, pBaseY);
        if (pBaseY > 0) AddAct(pBaseX, pBaseY - 1);
        if (pBaseY < UB_Y) AddAct(pBaseX, pBaseY + 1);

        return WillReturnList.ToArray();
    }

    //指定マスに隣接した座標を配列で返す
    static PointDef[] DeriveRinsetuPointArr(int pBaseX, int pBaseY)
    {
        var WillReturnList = new List<PointDef>();
        if (pBaseX > 0)
            WillReturnList.Add(new PointDef() { X = pBaseX - 1, Y = pBaseY });
        if (pBaseX < UB_X)
            WillReturnList.Add(new PointDef() { X = pBaseX + 1, Y = pBaseY });
        if (pBaseY > 0)
            WillReturnList.Add(new PointDef() { X = pBaseX, Y = pBaseY - 1 });
        if (pBaseY < UB_Y)
            WillReturnList.Add(new PointDef() { X = pBaseX, Y = pBaseY + 1 });

        return WillReturnList.ToArray();
    }

    //確定探索3 数値マスに隣接した空白マスが1マスの場合
    //戻り値として、有効な盤面かを返す
    static bool KakuteiTansaku3(char[,] pBanArr, char[,] pHebiIDArr)
    {
        for (int LoopX = 0; LoopX <= UB_X; LoopX++) {
            for (int LoopY = 0; LoopY <= UB_Y; LoopY++) {

                //カレントマスが数字マスでなかったら、処理しない
                char CurrMasu = pBanArr[LoopX, LoopY];
                if (CurrMasu < '1' || '5' < CurrMasu) continue;

                //カレントマスがヘビID採番済だったら、処理しない
                if (pHebiIDArr[LoopX, LoopY] != ' ') continue;

                //カレントマスに隣接した空白マスの座標Arr
                PointDef[] wkRinsetuSpaceArr = DeriveRinsetuSpacePointArr(pBanArr, LoopX, LoopY);

                //カレントマスに隣接した空白マスが1マスでなければ確定しない
                if (wkRinsetuSpaceArr.Length != 1) continue;

                //指定マスに隣接した盤面の値
                char[] wkRinsetuArr = DeriveRinsetuMasuArr(pBanArr, LoopX, LoopY);

                //確定処理を行い、最終的に有効な盤面かを返す
                Func<char, char, bool> KakuteiFunc = (pCurrMasu, pNeedMasu) =>
                {
                    if (CurrMasu == pCurrMasu && wkRinsetuArr.Contains(pNeedMasu) == false) {
                        pBanArr[wkRinsetuSpaceArr[0].X, wkRinsetuSpaceArr[0].Y] = pNeedMasu;

                        //埋めたマスをチェック
                        if (IsValid(pBanArr, LoopX, LoopY) == false) return false;

                        //埋めたマスの4近傍をチェック
                        PointDef[] RinsetuPointArr = DeriveRinsetuPointArr(LoopX, LoopY);
                        if (Array.Exists(RinsetuPointArr, A => IsValid(pBanArr, A.X, A.Y) == false))
                            return false;

                        //1か2をセットする場合のヘビの視線の先のチェック
                        if (SetHebiSisen(pBanArr, LoopX, LoopY) == false) return false;

                        //ヘビIDの採番と、ヘビ同士の隣接チェック
                        if (SetHebiID(pBanArr, pHebiIDArr, LoopX, LoopY) == false) return false;
                    }
                    return true;
                };
                if (KakuteiFunc('1', '2') == false) return false;
                if (KakuteiFunc('2', '1') == false) return false;
                if (KakuteiFunc('2', '3') == false) return false;
                if (KakuteiFunc('3', '2') == false) return false;
                if (KakuteiFunc('3', '4') == false) return false;
                if (KakuteiFunc('4', '3') == false) return false;
                if (KakuteiFunc('4', '5') == false) return false;
                if (KakuteiFunc('5', '4') == false) return false;
            }
        }
        return true;
    }

    //ヘビIDを列挙して、ヘビ同士の隣接管理
    //このようなパターンを想定する必要あり
    //   12345   21
    //1234      534
    //   5
    //計算量を減らすため、カレントの座標を引数としてもらい、そこからUnionFind
    static bool SetHebiID(char[,] pBanArr, char[,] pHebiIDArr, int pX, int pY)
    {
        //ヘビID採番済の場合
        if (pHebiIDArr[pX, pY] != ' ') return true;

        var NumPointList = new List<PointDef>();

        var Stk = new Stack<PointDef>();
        PointDef WillPush;

        Action<int, int> PushSyori = (pNewX, pNewY) =>
        {
            //数字マスでない場合
            char CurrMasu = pBanArr[pNewX, pNewY];
            if (CurrMasu < '1' || '5' < CurrMasu) return;

            //訪問済なら再訪できない
            if (NumPointList.Exists(A => A.X == pNewX && A.Y == pNewY))
                return;
            NumPointList.Add(new PointDef() { X = pNewX, Y = pNewY });

            WillPush.X = pNewX;
            WillPush.Y = pNewY;
            Stk.Push(WillPush);
        };

        PushSyori(pX, pY);
        while (Stk.Count > 0) {
            PointDef Popped = Stk.Pop();

            if (Popped.X > 0) PushSyori(Popped.X - 1, Popped.Y); //左に移動
            if (Popped.X < UB_X) PushSyori(Popped.X + 1, Popped.Y); //右に移動
            if (Popped.Y > 0) PushSyori(Popped.X, Popped.Y - 1); //上に移動
            if (Popped.Y < UB_Y) PushSyori(Popped.X, Popped.Y + 1); //下に移動
        }

        //6以上ならNG
        if (NumPointList.Count >= 6) return false;

        //4以下なら採番処理しない
        if (NumPointList.Count <= 4) return true;

        //1から5がそろってなかったらNG
        for (char I = '1'; I <= '5'; I++)
            if (NumPointList.Exists(A => pBanArr[A.X, A.Y] == I) == false)
                return false;

        //次の数字と隣接してなかったらNG
        foreach (PointDef EachPoint in NumPointList) {
            //指定マスに隣接した盤面の値の配列
            char[] RinsetuMasuArr = DeriveRinsetuMasuArr(pBanArr, EachPoint.X, EachPoint.Y);

            char CurrMasu = pBanArr[EachPoint.X, EachPoint.Y];
            for (char I = '1'; I <= '4'; I++)
                if (CurrMasu == I && Array.Exists(RinsetuMasuArr, A => A == I + 1) == false)
                    return false;
        }

        //確定空白をセット
        foreach (PointDef EachPoint1 in NumPointList) {
            //指定マスに隣接した空白の座標の配列
            PointDef[] RinsetuSpacePointArr =
                DeriveRinsetuSpacePointArr(pBanArr, EachPoint1.X, EachPoint1.Y);

            foreach (PointDef EachPoint2 in RinsetuSpacePointArr)
                pBanArr[EachPoint2.X, EachPoint2.Y] = '/';
        }

        //ヘビIDをセット
        char NewHebiID = DeriveNewHebiID(pHebiIDArr);
        foreach (PointDef EachPoint in NumPointList) {
            pHebiIDArr[EachPoint.X, EachPoint.Y] = NewHebiID;
        }
        return true;
    }

    //新しいヘビIDを採番する
    static char DeriveNewHebiID(char[,] pHebiIDArr)
    {
        IEnumerable<char> Tmp1 = pHebiIDArr.Cast<char>().Where(A => A != ' ');
        if (Tmp1.Any() == false) return '1';

        char CurrMax = Tmp1.Max();
        if (CurrMax <= '8') return ++CurrMax;
        if (CurrMax == '9') return 'A';
        return ++CurrMax;
    }

    //1か2の座標を引数として、1と2が揃ったら、視線の先を確定空白にする。
    //確定空白をセットできなかったらFalseを返す
    static bool SetHebiSisen(char[,] pBanArr, int pX, int pY)
    {
        //カレントマスが1でも2でもなかったら、チェックOK
        char CurrMasu = pBanArr[pX, pY];
        if (CurrMasu != '1' && CurrMasu != '2') return true;

        //指定マスに隣接した座標の配列
        PointDef[] RinsetuPointArr = DeriveRinsetuPointArr(pX, pY);

        //指定マスに隣接した盤面の値
        char[] wkRinsetuArr = RinsetuPointArr.Select(A => pBanArr[A.X, A.Y]).ToArray();

        //1と2が、まだ揃ってない場合
        if (CurrMasu == '1' && wkRinsetuArr.Contains('2') == false) return true;
        if (CurrMasu == '2' && wkRinsetuArr.Contains('1') == false) return true;

        //数字1と数字2の座標
        int X1 = -1, Y1 = -1, X2 = -1, Y2 = -1;

        if (CurrMasu == '1') {
            X1 = pX; Y1 = pY;
            PointDef wkPoint = RinsetuPointArr.Where(A => pBanArr[A.X, A.Y] == '2').Single();
            X2 = wkPoint.X; Y2 = wkPoint.Y;
        }
        if (CurrMasu == '2') {
            X2 = pX; Y2 = pY;
            PointDef wkPoint = RinsetuPointArr.Where(A => pBanArr[A.X, A.Y] == '1').Single();
            X1 = wkPoint.X; Y1 = wkPoint.Y;
        }

        //変位ベクトルの値
        int Heni_X = X1 - X2, Heni_Y = Y1 - Y2;

        //次のマスからが埋める候補となる
        int LoopX = X1 + Heni_X, LoopY = Y1 + Heni_Y;
        while (true) {
            //範囲外の場合
            if (LoopX < 0 || UB_X < LoopX) return true;
            if (LoopY < 0 || UB_Y < LoopY) return true;

            //黒マスの場合
            if (pBanArr[LoopX, LoopY] == '*') return true;

            bool IsOK = false;
            if (pBanArr[LoopX, LoopY] == '/') IsOK = true;
            if (pBanArr[LoopX, LoopY] == ' ') {
                pBanArr[LoopX, LoopY] = '/';
                IsOK = true;
            }

            if (IsOK == false) return false;

            LoopX += Heni_X; LoopY += Heni_Y;
        }
    }

    //確定探索4 矢印の数値マスの1つ前を確定空白に設定
    static void KakuteiTansaku4(char[,] pBanArr)
    {
        foreach (ArrowInfoDef EachArrowInfo in mArrowInfoArr) {
            //埋める値
            char FillNum = EachArrowInfo.NumVal;

            //数値が0なら対象外
            if (FillNum == '0') continue;

            //矢印の数値が埋められてなければ対象外
            int wkInd = Array.FindIndex(EachArrowInfo.PointArr, A => pBanArr[A.X, A.Y] != ' '
                                                                  && pBanArr[A.X, A.Y] != '/');
            if (wkInd >= 0) {
                if (pBanArr[EachArrowInfo.PointArr[wkInd].X,
                            EachArrowInfo.PointArr[wkInd].Y] != FillNum) {
                    continue;
                }
            }
            //後続マスは、確定空白となる
            if (wkInd >= 1) pBanArr[EachArrowInfo.PointArr[wkInd - 1].X,
                                    EachArrowInfo.PointArr[wkInd - 1].Y] = '/';
        }
    }

    //第4フェーズ
    //確定探索付の深さ優先探索
    static void ExecDFS(char[,] pBanArr, char[,] pHebiIDArr)
    {
        var Stk = new Stack<JyoutaiDef>();
        JyoutaiDef WillPush;
        WillPush.BanArr = pBanArr;
        WillPush.HebiIDArr = pHebiIDArr;
        WillPush.CurrX = WillPush.CurrY = 0;
        Stk.Push(WillPush);

        while (Stk.Count > 0) {
            JyoutaiDef Popped = Stk.Pop();

            char NeedMasu; //隣接が必要な数字

            //必要な数字と隣接してない座標を求める
            PointDef NextNeedNumPoint =
                DeriveNextNeedNumPoint(Popped.BanArr, Popped.HebiIDArr,
                                       Popped.CurrX, Popped.CurrY, out NeedMasu);

            //数値を埋めてない矢印情報を求める
            int NonFillNumArrowInfoInd = DeriveNonFillNumArrowInfo(Popped.BanArr);

            //クリア判定
            if (NextNeedNumPoint.X == -1 && NextNeedNumPoint.Y == -1
             && NonFillNumArrowInfoInd == -1) {
                Console.WriteLine("解を発見。経過時間={0}", sw.Elapsed);

                Console.WriteLine("数字の配置");
                PrintAnswer(Popped.BanArr);

                Console.WriteLine("ヘビIDの設定結果");
                PrintAnswer(Popped.HebiIDArr);
                return;
            }

            //矢印の数値を埋める探索と、必要な数字を埋める探索の、2通りの探索の内、
            //矢印の数値を埋める探索を行うか?
            bool WillExecArrowNumTansaku;

            //片方の探索のみが候補の場合
            if (NextNeedNumPoint.X == -1 && NextNeedNumPoint.Y == -1) {
                WillExecArrowNumTansaku = true;
            }
            else WillExecArrowNumTansaku = false;

            //両方の探索が候補の場合は、カレント座標の原点からの距離が小さいほうの探索とする
            if (NextNeedNumPoint.X > -1 && NextNeedNumPoint.Y > -1
             && NonFillNumArrowInfoInd > -1) {
                PointDef wkPoint1 = NextNeedNumPoint;
                int wkKyori1 = wkPoint1.X * wkPoint1.X + wkPoint1.Y * wkPoint1.Y;

                PointDef wkPoint2 = mArrowInfoArr[NonFillNumArrowInfoInd].Point;
                int wkKyori2 = wkPoint2.X * wkPoint2.X + wkPoint2.Y * wkPoint2.Y;

                WillExecArrowNumTansaku = (wkKyori1 > wkKyori2);
            }

            if (WillExecArrowNumTansaku)
                ExecPushSyori1(Stk, Popped, mArrowInfoArr[NonFillNumArrowInfoInd]);
            else ExecPushSyori2(Stk, Popped, NextNeedNumPoint, NeedMasu);
        }
    }

    //深さ優先探索で、CurrXとCurrYを引数として
    //必要な数字と隣接してない座標を求める
    static PointDef DeriveNextNeedNumPoint(char[,] pBanArr, char[,] pHebiIDArr, int pCurrX, int pCurrY,
                                           out char pNeedNum)
    {
        pNeedNum = '\0';

        for (int LoopY = pCurrY; LoopY <= UB_Y; LoopY++) {
            for (int LoopX = 0; LoopX <= UB_X; LoopX++) {
                if (pCurrY == LoopY && LoopX < pCurrX) continue;

                //カレントマスが数字マスでなかったら、処理しない
                char CurrMasu = pBanArr[LoopX, LoopY];
                if (CurrMasu < '1' || '5' < CurrMasu) continue;

                //カレントマスがヘビID採番済だったら、処理しない
                if (pHebiIDArr[LoopX, LoopY] != ' ') continue;

                //指定マスに隣接した盤面の値
                char[] wkRinsetuArr = DeriveRinsetuMasuArr(pBanArr, LoopX, LoopY);

                //必要な数字と隣接しているかをチェック
                Func<char, char, bool> CheckNeedNumFunc = (pCurrMasu, pFuncNeedNum) =>
                {
                    if (CurrMasu == pCurrMasu && wkRinsetuArr.Contains(pFuncNeedNum) == false) {
                        return true;
                    }
                    return false;
                };

                var CurrPoint = new PointDef() { X = LoopX, Y = LoopY };
                if (CheckNeedNumFunc('1', '2')) { pNeedNum = '2'; return CurrPoint; }
                if (CheckNeedNumFunc('2', '1')) { pNeedNum = '1'; return CurrPoint; }
                if (CheckNeedNumFunc('2', '3')) { pNeedNum = '3'; return CurrPoint; }
                if (CheckNeedNumFunc('3', '2')) { pNeedNum = '2'; return CurrPoint; }
                if (CheckNeedNumFunc('3', '4')) { pNeedNum = '4'; return CurrPoint; }
                if (CheckNeedNumFunc('4', '3')) { pNeedNum = '3'; return CurrPoint; }
                if (CheckNeedNumFunc('4', '5')) { pNeedNum = '5'; return CurrPoint; }
                if (CheckNeedNumFunc('5', '4')) { pNeedNum = '4'; return CurrPoint; }
            }
        }
        return new PointDef() { X = -1, Y = -1 };
    }

    //数値を埋めてない矢印情報の添字を求める
    static int DeriveNonFillNumArrowInfo(char[,] pBanArr)
    {
        for (int I = 0; I <= mArrowInfoArr.GetUpperBound(0); I++) {
            //埋める値
            char FillNum = mArrowInfoArr[I].NumVal;

            //数値が0なら対象外
            if (FillNum == '0') continue;

            //既に矢印の数値が埋められていたら対象外
            int wkInd = Array.FindIndex(mArrowInfoArr[I].PointArr, A => pBanArr[A.X, A.Y] != ' '
                                                                    && pBanArr[A.X, A.Y] != '/');
            bool IsFilled = false;
            if (wkInd >= 0) {
                if (pBanArr[mArrowInfoArr[I].PointArr[wkInd].X,
                            mArrowInfoArr[I].PointArr[wkInd].Y] == FillNum) {
                    IsFilled = true;
                }
            }
            if (IsFilled == false) return I;
        }
        return -1;
    }

    //矢印の数値を埋める探索のPush処理を行う
    static void ExecPushSyori1(Stack<JyoutaiDef> pStk,
                               JyoutaiDef pPopped,
                               ArrowInfoDef pArrowInfo)
    {
        char[,] wkBan = (char[,])pPopped.BanArr.Clone();

        foreach (PointDef EachPoint in pArrowInfo.PointArr) {
            int TargetX = EachPoint.X, TargetY = EachPoint.Y;
            char CurrMasu = wkBan[TargetX, TargetY];

            if (CurrMasu == '*') break;
            else if (CurrMasu == '/') continue;
            else if (wkBan[TargetX, TargetY] == ' ') { //数字を設定する場合
                JyoutaiDef WillPush;
                WillPush.BanArr = (char[,])wkBan.Clone();
                WillPush.BanArr[TargetX, TargetY] = pArrowInfo.NumVal;
                WillPush.HebiIDArr = (char[,])pPopped.HebiIDArr.Clone();
                WillPush.CurrX = Math.Min(TargetX, pPopped.CurrX);
                WillPush.CurrY = Math.Min(TargetY, pPopped.CurrY);

                //埋めたマスをチェック
                if (IsValid(WillPush.BanArr, TargetX, TargetY)) {
                    if (ExecKakuteiTansaku(WillPush.BanArr, WillPush.HebiIDArr)) {
                        pStk.Push(WillPush);
                    }
                }

                //後続マスは、確定空白となる
                wkBan[TargetX, TargetY] = '/';
            }
            else break;
        }
    }

    //必要な数字を埋める探索のPush処理を行う
    static void ExecPushSyori2(Stack<JyoutaiDef> pStk,
                               JyoutaiDef pPopped,
                               PointDef pNextNeedNumPoint,
                               char pNeedMasu)
    {
        //指定マスに隣接した空白マスの座標Arr
        PointDef[] wkRinsetuSpaceArr =
            DeriveRinsetuSpacePointArr(pPopped.BanArr, pNextNeedNumPoint.X, pNextNeedNumPoint.Y);

        foreach (PointDef EachPoint in wkRinsetuSpaceArr) {
            JyoutaiDef WillPush;

            WillPush.BanArr = (char[,])pPopped.BanArr.Clone();
            WillPush.BanArr[EachPoint.X, EachPoint.Y] = pNeedMasu;

            //埋めたマスをチェック
            if (IsValid(WillPush.BanArr, EachPoint.X, EachPoint.Y) == false)
                continue;

            //埋めたマスの4近傍をチェック
            PointDef[] RinsetuPointArr = DeriveRinsetuPointArr(EachPoint.X, EachPoint.Y);
            if (Array.Exists(RinsetuPointArr, A => IsValid(WillPush.BanArr, A.X, A.Y) == false))
                continue;

            //1か2をセットする場合のヘビの視線の先のチェック
            if (SetHebiSisen(WillPush.BanArr, EachPoint.X, EachPoint.Y) == false)
                continue;

            //ヘビIDの採番と、ヘビ同士の隣接チェック
            WillPush.HebiIDArr = (char[,])pPopped.HebiIDArr.Clone();
            if (SetHebiID(WillPush.BanArr, WillPush.HebiIDArr, EachPoint.X, EachPoint.Y) == false)
                continue;

            WillPush.CurrX = Math.Min(EachPoint.X, pPopped.CurrX);
            WillPush.CurrY = Math.Min(EachPoint.Y, pPopped.CurrY);
            pStk.Push(WillPush);
        }
    }

    //解を出力
    static void PrintAnswer(char[,] pBanArr)
    {
        //半角数字を全角数字に変換
        Func<char, char> SingleToWideFunc = (pStr) => (char)('0' + pStr - '0');

        var sb = new System.Text.StringBuilder();
        for (int Y = 0; Y <= UB_Y; Y++) {
            for (int X = 0; X <= UB_X; X++) {
                char CurrMasu1 = QuestionArrowArr[X, Y];
                if (CurrMasu1 != '□') {
                    sb.Append(CurrMasu1);
                    continue;
                }

                char CurrMasu2 = pBanArr[X, Y];
                if (CurrMasu2 == ' ') sb.Append('□');
                else if (CurrMasu2 == '/') sb.Append('/');
                else sb.Append(SingleToWideFunc(CurrMasu2));
            }
            sb.AppendLine();
        }
        Console.WriteLine(sb.ToString());
    }
}


実行結果

解を発見。経過時間=00:00:00.1732831
数字の配置
///12
■45→3
23↑54
1↓□/↑
//□□□

ヘビIDの設定結果
□□□11
■22→1
22↑11
2↓□□↑
□□□□□


追加問題02 パズルBox10の例題

char[,] Q02_ArrowArr = {{'□','↓','■','□','□','□','□'},
                        {'□','□','□','□','→','□','□'},
                        {'□','■','□','■','□','□','↑'},
                        {'□','□','□','□','□','□','□'},
                        {'↓','□','□','↓','□','→','□'},
                        {'□','□','←','□','□','□','□'},
                        {'□','□','□','□','■','■','□'}};

char[,] Q02_BanNumArr = {{' ','5' ,' ' ,' ' ,' ' ,' ' ,' '},
                         {' ',' ' ,' ' ,' ' ,'0' ,' ' ,' '},
                         {' ',' ' ,' ' ,' ' ,' ' ,' ' ,'5'},
                         {' ',' ' ,' ' ,' ' ,' ' ,' ' ,' '},
                         {'2',' ' ,' ' ,'5' ,' ' ,'0' ,' '},
                         {' ',' ' ,'3' ,' ' ,' ' ,' ' ,' '},
                         {' ',' ' ,' ' ,' ' ,' ' ,' ' ,' '}};


追加問題03 パズルBox10の01問目

char[,] Q03_ArrowArr = {{'□','□','□','□','←','■','□','□','□','□'},
                        {'↑','↓','□','□','□','→','□','□','■','□'},
                        {'□','□','□','←','□','□','□','■','■','□'},
                        {'□','→','□','□','□','←','□','□','□','□'},
                        {'□','□','□','□','□','■','□','■','□','■'},
                        {'■','□','■','□','■','□','□','□','□','□'},
                        {'□','□','□','□','→','□','□','□','↓','□'},
                        {'□','↑','↓','□','□','□','→','□','□','□'},
                        {'□','←','□','□','■','□','□','□','←','■'},
                        {'□','□','□','□','■','↑','□','□','□','□'}};

char[,] Q03_BanNumArr = {{' ',' ' ,' ' ,' ' ,'3' ,' ' ,' ' ,' ' ,' ' ,' '},
                         {'5','0' ,' ' ,' ' ,' ' ,'5' ,' ' ,' ' ,' ' ,' '},
                         {' ',' ' ,' ' ,'5' ,' ' ,' ' ,' ' ,' ' ,' ' ,' '},
                         {' ','5' ,' ' ,' ' ,' ' ,'4' ,' ' ,' ' ,' ' ,' '},
                         {' ',' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' '},
                         {' ',' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' '},
                         {' ',' ' ,' ' ,' ' ,'0' ,' ' ,' ' ,' ' ,'5' ,' '},
                         {' ','5' ,'2' ,' ' ,' ' ,' ' ,'5' ,' ' ,' ' ,' '},
                         {' ','5' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,'4' ,' '},
                         {' ',' ' ,' ' ,' ' ,' ' ,'5' ,' ' ,' ' ,' ' ,' '}};


追加問題04 パズルBox10の05問目

char[,] Q04_ArrowArr = {{'□','□','□','□','□','□','→','□','→','□'},
                        {'→','□','↑','□','□','□','□','□','□','□'},
                        {'□','□','□','□','□','↓','□','□','□','□'},
                        {'□','→','□','□','□','□','□','□','□','↑'},
                        {'□','□','□','→','□','→','□','→','□','□'},
                        {'□','↓','□','□','□','□','□','□','□','□'},
                        {'↓','□','□','□','□','□','□','□','□','□'},
                        {'□','□','□','□','□','□','□','□','□','□'},
                        {'↓','□','■','□','→','□','□','□','□','□'},
                        {'□','□','□','□','→','□','□','□','□','□'}};

char[,] Q04_BanNumArr = {{' ',' ' ,' ' ,' ' ,' ' ,' ' ,'1' ,' ' ,'5' ,' '},
                         {'3',' ' ,'1' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' '},
                         {' ',' ' ,' ' ,' ' ,' ' ,'3' ,' ' ,' ' ,' ' ,' '},
                         {' ','5' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,'3'},
                         {' ',' ' ,' ' ,'5' ,' ' ,'1' ,' ' ,'5' ,' ' ,' '},
                         {' ','3' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' '},
                         {'1',' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' '},
                         {' ',' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' '},
                         {'5',' ' ,' ' ,' ' ,'5' ,' ' ,' ' ,' ' ,' ' ,' '},
                         {' ',' ' ,' ' ,' ' ,'5' ,' ' ,' ' ,' ' ,' ' ,' '}};


追加問題05 パズルBox10の07問目

char[,] Q05_ArrowArr = {
{'□','□','□','□','↓','□','□','□','□','□','□','□','←','□','□','□','□'},
{'□','←','→','□','□','□','→','□','↑','□','□','□','□','□','□','←','□'},
{'□','□','□','□','←','□','□','□','←','□','→','□','↑','□','□','←','□'},
{'□','□','□','→','□','□','□','□','□','□','□','□','□','→','□','□','□'},
{'↓','□','←','□','□','□','□','↓','□','←','→','□','□','□','←','□','■'},
{'□','□','□','□','□','■','□','□','□','□','□','■','□','□','□','□','□'},
{'□','□','↑','□','■','□','□','■','■','□','□','□','□','□','□','←','□'},
{'□','□','□','□','↓','□','□','□','□','□','■','□','←','□','□','□','□'},
{'□','■','↓','□','□','□','→','□','■','□','←','□','□','□','■','■','□'},
{'□','□','□','□','↓','□','■','□','□','□','□','□','■','□','□','□','□'},
{'□','←','□','□','□','□','□','□','■','■','□','□','■','□','→','□','□'},
{'□','□','□','□','□','→','□','□','□','□','□','→','□','□','□','□','□'},
{'↑','□','→','□','□','□','↓','↑','□','■','□','□','□','□','■','□','↓'},
{'□','□','□','↑','□','□','□','□','□','□','□','□','□','■','□','□','□'},
{'□','←','□','□','←','□','■','□','■','□','□','□','■','□','□','□','□'},
{'□','■','□','□','□','□','□','□','■','□','↓','□','□','□','↓','→','□'},
{'□','□','□','□','↑','□','□','□','□','□','□','□','■','□','□','□','□'}};

char[,] Q05_BanNumArr = {
{' ',' ' ,' ' ,' ' ,'4' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,'5' ,' ' ,' ' ,' ' ,' '},
{' ','1' ,'3' ,' ' ,' ' ,' ' ,'1' ,' ' ,'3' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,'2' ,' '},
{' ',' ' ,' ' ,' ' ,'2' ,' ' ,' ' ,' ' ,'5' ,' ' ,'0' ,' ' ,'3' ,' ' ,' ' ,'0' ,' '},
{' ',' ' ,' ' ,'4' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,'2' ,' ' ,' ' ,' '},
{'2',' ' ,'0' ,' ' ,' ' ,' ' ,' ' ,'0' ,' ' ,'4' ,'4' ,' ' ,' ' ,' ' ,'3' ,' ' ,' '},
{' ',' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' '},
{' ',' ' ,'4' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,'1' ,' '},
{' ',' ' ,' ' ,' ' ,'3' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,'5' ,' ' ,' ' ,' ' ,' '},
{' ',' ' ,'1' ,' ' ,' ' ,' ' ,'3' ,' ' ,' ' ,' ' ,'3' ,' ' ,' ' ,' ' ,' ' ,' ' ,' '},
{' ',' ' ,' ' ,' ' ,'5' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' '},
{' ','3' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,'5' ,' ' ,' '},
{' ',' ' ,' ' ,' ' ,' ' ,'4' ,' ' ,' ' ,' ' ,' ' ,' ' ,'4' ,' ' ,' ' ,' ' ,' ' ,' '},
{'4',' ' ,'5' ,' ' ,' ' ,' ' ,'3' ,'5' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,'5'},
{' ',' ' ,' ' ,'3' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' '},
{' ','1' ,' ' ,' ' ,'0' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' '},
{' ',' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,'1' ,' ' ,' ' ,' ' ,'1' ,'1' ,' '},
{' ',' ' ,' ' ,' ' ,'4' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' '}};


追加問題06 パズルBox10の08問目

char[,] Q06_ArrowArr = {
{'□','←','□','□','□','□','□','□','□','□','□','□','□','→','□','□','□'},
{'□','□','□','←','□','□','□','□','→','□','□','□','□','□','□','↑','□'},
{'■','□','□','□','□','□','□','↑','□','□','□','■','□','□','□','■','□'},
{'□','□','□','□','□','□','↑','→','□','↑','□','←','□','↑','□','□','□'},
{'□','→','□','■','□','□','□','□','□','■','□','■','□','■','□','↑','□'},
{'□','□','□','□','□','→','□','□','■','↓','□','↓','□','■','□','■','□'},
{'↑','□','■','■','□','□','□','□','□','□','□','□','□','□','□','□','□'},
{'□','□','□','□','←','□','□','←','→','□','□','□','→','□','□','□','←'},
{'□','■','□','□','□','□','■','□','□','□','↓','□','■','□','■','□','□'},
{'□','■','□','■','□','□','□','□','□','□','□','□','□','□','□','□','■'},
{'□','■','□','■','→','□','□','□','←','■','□','□','□','□','□','□','□'},
{'□','□','□','←','↓','□','□','□','□','□','□','□','□','■','□','□','□'},
{'□','■','□','↓','□','□','□','↑','□','□','□','→','□','□','↓','□','□'},
{'□','←','□','□','□','■','□','■','↓','□','■','□','□','□','□','□','□'},
{'□','■','□','□','□','□','□','←','□','□','□','■','↓','□','□','□','□'},
{'□','↓','□','□','□','↓','■','□','□','□','□','□','□','□','↓','→','□'},
{'□','□','□','□','□','□','□','□','□','□','■','□','□','□','□','□','□'}};

char[,] Q06_BanNumArr = {
{' ','5' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,'2' ,' ' ,' ' ,' '},
{' ',' ' ,' ' ,'3' ,' ' ,' ' ,' ' ,' ' ,'3' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,'3' ,' '},
{' ',' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,'0' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' '},
{' ',' ' ,' ' ,' ' ,' ' ,' ' ,'5' ,'3' ,' ' ,'5' ,' ' ,'1' ,' ' ,'5' ,' ' ,' ' ,' '},
{' ','1' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,'5' ,' '},
{' ',' ' ,' ' ,' ' ,' ' ,'5' ,' ' ,' ' ,' ' ,'5' ,' ' ,'1' ,' ' ,' ' ,' ' ,' ' ,' '},
{'4',' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' '},
{' ',' ' ,' ' ,' ' ,'5' ,' ' ,' ' ,'0' ,'5' ,' ' ,' ' ,' ' ,'3' ,' ' ,' ' ,' ' ,'5'},
{' ',' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,'3' ,' ' ,' ' ,' ' ,' ' ,' ' ,' '},
{' ',' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' '},
{' ',' ' ,' ' ,' ' ,'2' ,' ' ,' ' ,' ' ,'3' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' '},
{' ',' ' ,' ' ,'1' ,'5' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' '},
{' ',' ' ,' ' ,'0' ,' ' ,' ' ,' ' ,'3' ,' ' ,' ' ,' ' ,'3' ,' ' ,' ' ,'5' ,' ' ,' '},
{' ','3' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,'4' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' '},
{' ',' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,'2' ,' ' ,' ' ,' ' ,' ' ,'3' ,' ' ,' ' ,' ' ,' '},
{' ','5' ,' ' ,' ' ,' ' ,'3' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,'1' ,'4' ,' '},
{' ',' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' ' ,' '}};


解説

矢印の数値を埋める探索と、必要な数字を埋める探索の、2通りの探索を作成し、
組み合わせ爆発が発生しにくいように、
カレント座標の原点からの距離が小さいほうの探索を選択してます。