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

19-02 矢っ解なパズル

問題

匹見パズルの矢っ解なパズルを解きます。



9枚の板を3*3に並べて、12個の長い矢印を作って下さい。
ただし、半分色の違う矢印は駄目。
答えは1通り。

答えの矢印


ソース

using System;
using System.Collections.Generic;

class Program
{
    const int UB = 8;

    struct PieceInfoDef
    {
        internal int PieceID;
        internal List<char[]> ColorArrList;
        internal List<bool[]> IsShitenArrList;
    }
    static PieceInfoDef[] mPieceInfoArr;

    struct JyoutaiDef
    {
        internal char[,] BanArrColor;
        internal bool[,] BanArrIsShiten;
        internal int Level;
        internal List<int> UsedPieceIDList;
    }

    static void Main()
    {
        //ピースごとの情報を取得
        DerivePieceInfo();

        //回転解の排除で、ピース1は回転させない
        mPieceInfoArr[0].ColorArrList.RemoveRange(1, mPieceInfoArr[0].ColorArrList.Count - 1);
        mPieceInfoArr[0].IsShitenArrList.RemoveRange(1, mPieceInfoArr[0].IsShitenArrList.Count - 1);

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

        WillPush.BanArrColor = new char[UB + 1, UB + 1];
        WillPush.BanArrIsShiten = new bool[UB + 1, UB + 1];
        for (int X = 0; X <= UB; X++)
            for (int Y = 0; Y <= UB; Y++)
                WillPush.BanArrColor[X, Y] = '□';
        WillPush.Level = 0;
        WillPush.UsedPieceIDList = new List<int>();
        Stk.Push(WillPush);

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

            //クリア判定
            if (Popped.Level == 9) {
                Console.WriteLine("解を発見");
                PrintBan(Popped.BanArrColor, Popped.BanArrIsShiten, Popped.UsedPieceIDList);
                continue;
            }

            int BaseX, BaseY;
            DeriveShikitumePos(Popped.Level, out BaseX, out BaseY);
            foreach (PieceInfoDef EachPieceInfo in mPieceInfoArr) {
                if (Popped.UsedPieceIDList.Contains(EachPieceInfo.PieceID))
                    continue;

                for (int I = 0; I <= EachPieceInfo.ColorArrList.Count - 1; I++) {
                    char[] wkColorArr = EachPieceInfo.ColorArrList[I];
                    WillPush.BanArrColor = (char[,])Popped.BanArrColor.Clone();
                    WillPush.BanArrColor[BaseX + 1, BaseY] = wkColorArr[0];
                    WillPush.BanArrColor[BaseX + 2, BaseY + 1] = wkColorArr[1];
                    WillPush.BanArrColor[BaseX + 1, BaseY + 2] = wkColorArr[2];
                    WillPush.BanArrColor[BaseX, BaseY + 1] = wkColorArr[3];

                    bool[] wkIsShitenArr = EachPieceInfo.IsShitenArrList[I];
                    WillPush.BanArrIsShiten = (bool[,])Popped.BanArrIsShiten.Clone();
                    WillPush.BanArrIsShiten[BaseX + 1, BaseY] = wkIsShitenArr[0];
                    WillPush.BanArrIsShiten[BaseX + 2, BaseY + 1] = wkIsShitenArr[1];
                    WillPush.BanArrIsShiten[BaseX + 1, BaseY + 2] = wkIsShitenArr[2];
                    WillPush.BanArrIsShiten[BaseX, BaseY + 1] = wkIsShitenArr[3];
                    if (IsValid(WillPush.BanArrColor, WillPush.BanArrIsShiten, Popped.Level) == false)
                        continue;

                    WillPush.Level = Popped.Level + 1;
                    WillPush.UsedPieceIDList = new List<int>(Popped.UsedPieceIDList);
                    WillPush.UsedPieceIDList.Add(EachPieceInfo.PieceID);

                    Stk.Push(WillPush);
                }
            }
        }
    }

    //ピースごとの情報を取得
    static void DerivePieceInfo()
    {
        var PieceInfoList = new List<PieceInfoDef>();

        Action<int, string, bool[]> AddAct = (pID, pStr, pArr) =>
        {
            PieceInfoDef WillAdd;
            WillAdd.PieceID = pID;
            WillAdd.ColorArrList = DeriveHaitiArrList(pStr.ToCharArray());
            WillAdd.IsShitenArrList = DeriveHaitiArrList(pArr);
            PieceInfoList.Add(WillAdd);
        };
        AddAct(1, "赤緑黄青", new bool[] { true, false, false, true });
        AddAct(2, "青赤黄緑", new bool[] { true, false, false, true });
        AddAct(3, "黄緑青赤", new bool[] { false, false, true, true });
        AddAct(4, "緑黄赤青", new bool[] { false, true, true, false });
        AddAct(5, "青黄赤緑", new bool[] { false, true, true, false });
        AddAct(6, "赤黄緑青", new bool[] { false, true, true, false });
        AddAct(7, "赤青緑黄", new bool[] { false, true, true, false });
        AddAct(8, "緑黄赤青", new bool[] { true, true, false, false });
        AddAct(9, "青緑赤黄", new bool[] { false, false, true, true });

        mPieceInfoArr = PieceInfoList.ToArray();
    }

    //回転させた配置を返す(4通り)
    static List<TypeName[]> DeriveHaitiArrList<TypeName>(TypeName[] pBaseArr)
    {
        var WillReturn = new List<TypeName[]>();

        for (int I = 0; I <= pBaseArr.GetUpperBound(0); I++) {
            var WillAdd = new List<TypeName>();
            int CurrInd = I;
            for (int J = 1; J <= pBaseArr.Length; J++) {
                WillAdd.Add(pBaseArr[I]);
                if (++I > pBaseArr.GetUpperBound(0)) I = 0;
            }
            WillReturn.Add(WillAdd.ToArray());
        }
        return WillReturn;
    }

    //レベルを引数として、敷き詰めの座標を返す
    static void DeriveShikitumePos(int pLevel, out int pX, out int pY)
    {
        pX = pY = -1;
        if (pLevel == 0) { pX = 0; pY = 0; }
        if (pLevel == 1) { pX = 3; pY = 0; }
        if (pLevel == 2) { pX = 6; pY = 0; }

        if (pLevel == 3) { pX = 0; pY = 3; }
        if (pLevel == 4) { pX = 3; pY = 3; }
        if (pLevel == 5) { pX = 6; pY = 3; }

        if (pLevel == 6) { pX = 0; pY = 6; }
        if (pLevel == 7) { pX = 3; pY = 6; }
        if (pLevel == 8) { pX = 6; pY = 6; }
    }

    //盤面とレベルを引数として、有効な盤面かをチェック
    static bool IsValid(char[,] pBanArrColor, bool[,] pBanArrIsShiten, int pLevel)
    {
        Func<int, int, int, int, bool> IsOKFunc = (pX1, pY1, pX2, pY2) =>
        {
            if (pBanArrColor[pX1, pY1] != pBanArrColor[pX2, pY2]) return false;
            if (pBanArrIsShiten[pX1, pY1] == pBanArrIsShiten[pX2, pY2]) return false;
            return true;
        };

        if (pLevel == 1 && IsOKFunc(2, 1, 3, 1) == false) return false;
        if (pLevel == 2 && IsOKFunc(5, 1, 6, 1) == false) return false;
        if (pLevel == 3 && IsOKFunc(1, 2, 1, 3) == false) return false;

        if (pLevel == 4 && IsOKFunc(2, 4, 3, 4) == false) return false;
        if (pLevel == 4 && IsOKFunc(4, 2, 4, 3) == false) return false;

        if (pLevel == 5 && IsOKFunc(5, 4, 6, 4) == false) return false;
        if (pLevel == 5 && IsOKFunc(7, 2, 7, 3) == false) return false;

        if (pLevel == 6 && IsOKFunc(1, 5, 1, 6) == false) return false;

        if (pLevel == 7 && IsOKFunc(2, 7, 3, 7) == false) return false;
        if (pLevel == 7 && IsOKFunc(4, 5, 4, 6) == false) return false;

        if (pLevel == 8 && IsOKFunc(5, 7, 6, 7) == false) return false;
        if (pLevel == 8 && IsOKFunc(7, 5, 7, 6) == false) return false;

        return true;
    }

    //盤面を出力
    static void PrintBan(char[,] pBanArrColor, bool[,] pBanArrIsShiten, List<int> pUsedPieceIDList)
    {
        var sb = new System.Text.StringBuilder();

        sb.AppendLine("ピースIDの配置");
        for (int I = 0; I <= pUsedPieceIDList.Count - 1; I++) {
            if (I == 3 || I == 6)
                sb.AppendLine();
            sb.Append(pUsedPieceIDList[I]);
        }
        sb.AppendLine();

        sb.AppendLine("色の配置");
        for (int Y = 0; Y <= UB; Y++) {
            for (int X = 0; X <= UB; X++) {
                sb.Append(pBanArrColor[X, Y]);
            }
            sb.AppendLine();
        }

        sb.AppendLine("矢印の配置");
        for (int Y = 0; Y <= UB; Y++) {
            for (int X = 0; X <= UB; X++) {
                bool wkBool = pBanArrIsShiten[X, Y];

                //3を法として分類
                int ModX = X % 3;
                int ModY = Y % 3;
                if (ModX == 1 && ModY == 0) {
                    sb.Append(wkBool ? '↓' : '|');
                }
                else if (ModX == 2 && ModY == 1) {
                    sb.Append(wkBool ? '←' : '−');
                }
                else if (ModX == 1 && ModY == 2) {
                    sb.Append(wkBool ? '↑' : '|');
                }
                else if (ModX == 0 && ModY == 1) {
                    sb.Append(wkBool ? '→' : '−');
                }
                else sb.Append('□');
            }
            sb.AppendLine();
        }
        Console.WriteLine(sb.ToString());
    }
}


実行結果

解を発見
ピースIDの配置
629
517
384
色の配置
□赤□□緑□□緑□
青□黄黄□青青□赤
□緑□□赤□□黄□
□緑□□赤□□黄□
赤□青青□緑緑□赤
□黄□□黄□□青□
□黄□□黄□□青□
赤□緑緑□赤赤□緑
□青□□青□□黄□
矢印の配置
□|□□↓□□|□
−□←−□←−□←
□↑□□|□□↑□
□|□□↓□□|□
→□−→□−→□−
□↑□□|□□↑□
□|□□↓□□|□
→□−→□−→□−
□↑□□|□□↑□


解説

深さ優先探索でピースを敷き詰めてます。