「#C#」タグアーカイブ

【C#】【ピクロス】【ALTSEED】解析パターンその8

前回までの状況はこちら。

最新ソースはこちら。(gitHub)

https://github.com/takishita2nd/Picross

今回注目したのはここです。

ここはどのように塗っていくかというと、

空いているマスが4マスに対して、数字は3なので、

真ん中の2マスは確実に塗れることが分かります。

なので、これを実装します。

処理を単純化させるため、解析対象となる数字が行、列で1つの場合のみを、このロジックの対象とします。

また、このロジックを適用するマスを抽出するのですが、

マスクとマスクの間が全て塗られていた場合は処理済みと判断してスキップさせます。

その結果、対象のマスが1つの場合のみ、このロジックの対象とします。

この様な説明で分かってもらえるかどうか分からないけど、そういうことをやっています。

        // 解析パターンその8
        // 数字が大きい場合、真ん中の方を塗る
        private void pattern8()
        {
            // Row
            pattern8Row();
            // Col
            pattern8Col();
        }

        private void pattern8Row()
        {
            int row = 0;
            foreach (var rowlist in rowNumbers)
            {
                if (rowlist.IsAnalyzed())
                {
                    row++;
                    continue;
                }

                // 有効な数字を取り出す
                List<int> values = new List<int>();
                foreach(var data in rowlist.AnalyzeDatas)
                {
                    if (data.IsAnalyzed())
                    {
                        continue;
                    }
                    values.Add(data.Value);
                }
                if(values.Count != 1)
                {
                    row++;
                    continue;
                }

                // 対象となるマスを抽出する
                List<List<BitmapData>> bitmapLists = new List<List<BitmapData>>();
                List<BitmapData> bitmaplist = new List<BitmapData>();
                for (int col = 0; col < colNumbers.Count; col++)
                {
                    // マスクとマスクの間が全て塗られていたら、そこは対象としない
                    if (_bitmapData[row, col].IsMasked())
                    {
                        if(bitmaplist.Count != 0)
                        {
                            bool done = true;
                            foreach(var bitmap in bitmaplist)
                            {
                                if(bitmap.IsPainted() == false)
                                {
                                    done = false;
                                }
                            }
                            if(done == false)
                            {
                                bitmapLists.Add(bitmaplist);
                            }
                            bitmaplist = new List<BitmapData>();
                        }
                        continue;
                    }
                    bitmaplist.Add(_bitmapData[row, col]);
                }
                if (bitmaplist.Count != 0)
                {
                    bitmapLists.Add(bitmaplist);
                }
                if(bitmapLists.Count != 1)
                {
                    row++;
                    continue;
                }

                int value = values[0];
                List<BitmapData> bitmaps = bitmapLists[0];
                if(value < bitmaps.Count && value * 2 > bitmaps.Count)
                {
                    int paintNum = value * 2 - bitmaps.Count;
                    int countMax = 0;
                    if(paintNum % 2 == 1)
                    {
                        countMax = paintNum / 2 + 1;
                    }
                    else
                    {
                        countMax = paintNum / 2;
                    }
                    for (int count = -paintNum / 2; count < countMax; count++)
                    {
                        bitmaps[bitmaps.Count / 2 + count].Paint();
                    }
                }
                row++;
            }
        }

        private void pattern8Col()
        {
            int col = 0;
            foreach (var collist in colNumbers)
            {
                if (collist.IsAnalyzed())
                {
                    col++;
                    continue;
                }

                // 有効な数字を取り出す
                List<int> values = new List<int>();
                foreach (var data in collist.AnalyzeDatas)
                {
                    if (data.IsAnalyzed())
                    {
                        continue;
                    }
                    values.Add(data.Value);
                }
                if (values.Count != 1)
                {
                    col++;
                    continue;
                }

                // 対象となるマスを抽出する
                List<List<BitmapData>> bitmapLists = new List<List<BitmapData>>();
                List<BitmapData> bitmaplist = new List<BitmapData>();
                for (int row = 0; row < rowNumbers.Count; row++)
                {
                    // マスクとマスクの間が全て塗られていたら、そこは対象としない
                    if (_bitmapData[row, col].IsMasked())
                    {
                        if (bitmaplist.Count != 0)
                        {
                            bool done = true;
                            foreach (var bitmap in bitmaplist)
                            {
                                if (bitmap.IsPainted() == false)
                                {
                                    done = false;
                                }
                            }
                            if (done == false)
                            {
                                bitmapLists.Add(bitmaplist);
                            }
                            bitmaplist = new List<BitmapData>();
                        }
                        continue;
                    }
                    bitmaplist.Add(_bitmapData[row, col]);
                }
                if (bitmaplist.Count != 0)
                {
                    bitmapLists.Add(bitmaplist);
                }
                if (bitmapLists.Count != 1)
                {
                    col++;
                    continue;
                }

                int value = values[0];
                List<BitmapData> bitmaps = bitmapLists[0];
                if (value < bitmaps.Count && value * 2 > bitmaps.Count)
                {
                    int paintNum = value * 2 - bitmaps.Count;
                    int countMax = 0;
                    if (paintNum % 2 == 1)
                    {
                        countMax = paintNum / 2 + 1;
                    }
                    else
                    {
                        countMax = paintNum / 2;
                    }
                    for (int count = -paintNum / 2; count < countMax; count++)
                    {
                        bitmaps[bitmaps.Count / 2 + count].Paint();
                    }
                }

                col++;
            }
        }
        public BitmapData[,] Run()
        {
            pattern1();
            while (checkPainedCount())
            {
                pattern2();
                pattern3();
                pattern4();
                pattern5();
                pattern6();
                pattern7();
                pattern8();
                doMask();
            }

            return _bitmapData;
        }

実行結果はこうなりました。

全て埋まりましたね。

解けたようです。

やったぁ。

【C#】【ピクロス】【ALTSEED】解析パターン5と6の修正

前回までの状況はこちら。

最新ソースはこちら。(gitHub)

https://github.com/takishita2nd/Picross

あれからがっつり修正しました。

具体的には、解析パターン5と6。

一度にたくさん処理していたのを、

一番最初のもの、一番最後のものの2パターンに分け、1回の処理では1つだけしか処理しない、という風にしました。

最初のループでは処理できない箇所でも次以降のループで処理できるでしょ、という考え。

詳しくはgitHubのソースコードを見て欲しい。

最終的には、こんな感じになりました。

解析完了までもう少しだが、まだ解析手法はあるはず。

これから考えます。

【C#】【ピクロス】【ALTSEED】解析パターンその7

前回までの状況はこちら。

最新ソースはこちら。(gitHub)

https://github.com/takishita2nd/Picross

今回はここを狙います。

ここは、数値が一つなのに対して、塗るで確定しているマスが二つありますので、この間が塗れることが分かります。

これを実装します。

        // 解析パターンその7
        // 数字が1個で2マス塗られている場合はその間を塗る
        private void pattern7()
        {
            // Row
            pattern7Row();
            // Col
            pattern7Col();
        }

        private void pattern7Row()
        {
            int row = 0;
            foreach (var rowlist in rowNumbers)
            {
                if (rowlist.IsAnalyzed())
                {
                    row++;
                    continue;
                }

                if(rowlist.AnalyzeDatas.Count == 1)
                {
                    int value = rowlist.AnalyzeDatas[0].Value;
                    int startCol = 0;
                    int endCol = 0;
                    for (int col = 0; col < colNumbers.Count; col++)
                    {
                        if(_bitmapData[row, col].IsPainted() && startCol == 0)
                        {
                            startCol = col;
                        }
                        else if(_bitmapData[row, col].IsMasked() && startCol != 0)
                        {
                            break;
                        }
                        else if(_bitmapData[row, col].IsPainted() && startCol != 0)
                        {
                            endCol = col;
                        }
                    }

                    if(startCol== 0 || endCol == 0)
                    {
                        row++;
                        continue;
                    }

                    for(int col = startCol; col < endCol; col++)
                    {
                        _bitmapData[row, col].Paint();
                    }
                }
                row++;
            }
        }

        private void pattern7Col()
        {
            int col = 0;
            foreach (var collist in colNumbers)
            {
                if (collist.IsAnalyzed())
                {
                    col++;
                    continue;
                }

                if (collist.AnalyzeDatas.Count == 1)
                {
                    int value = collist.AnalyzeDatas[0].Value;
                    int startRow = 0;
                    int endRow = 0;
                    for (int row = 0; row < rowNumbers.Count; row++)
                    {
                        if (_bitmapData[row, col].IsPainted() && startRow == 0)
                        {
                            startRow = row;
                        }
                        else if (_bitmapData[row, col].IsMasked() && startRow != 0)
                        {
                            break;
                        }
                        else if (_bitmapData[row, col].IsPainted() && startRow != 0)
                        {
                            endRow = row;
                        }
                    }

                    if (startRow == 0 || endRow == 0)
                    {
                        col++;
                        continue;
                    }

                    for (int row = startRow; row < endRow; row++)
                    {
                        _bitmapData[row, col].Paint();
                    }
                }
                col++;
            }
        }

やっていることは簡単で、最初に塗られているマスと後に塗られているマスを探し、その間を塗る、というロジックです。

まずは1周実行してみます。

想定通りに塗られていますね。

では2周目

3周目

おや?

ここをマスクされると、縦11の数字の処理に矛盾が発生しますので、この動きはおかしいですね。

いろいろ調査してみましたが、解析パターン5でマスクを行っている用です。

なので、今の解析パターン5に問題があるようです。

なので、次回なおしましょうか。(今なおしたらハマりそう)

【C#】【ピクロス】【Altseed】新しい問題に取り組む

次はこの問題を解いてみようと思います。

これまで作成したアルゴリズムを適用してみました。

まぁ、そうだろうねぇ。

でもこれならまだ頭の中に解法はある。

次回からそれを実装していきましょう。

あ、ロード処理を少し修正しました。

最新ソースはこちら。(gitHub)

https://github.com/takishita2nd/Picross

【C#】【ピクロス】【ALTSEED】解析パターンその6

前回までの状況はこちら。

最新ソースはこちら。(gitHub)

https://github.com/takishita2nd/Picross

前回の続き。

前回は前から塗れるところをチェックしていきましたが、今回は後ろから塗れるところをチェックしていきます。

この時きちんと考えなくちゃいけないのは、データの順序。

今回、ここがぐっちゃになって一番ハマった。

前回のソースにも関連するのですが、

            foreach (var rowlist in rowNumbers)
            {
                if (rowlist.IsAnalyzed())
                {
                    row++;
                    continue;
                }

このrowlistをReverse()すると、元々の解析データ(rowNumbers)が壊れてしまうので、

            foreach (var rowlist in rowNumbers)
            {
                if (rowlist.IsAnalyzed())
                {
                    row++;
                    continue;
                }
                var tempRowList = rowlist.Clone();
                tempRowList.AnalyzeDatas.Reverse();

こんな感じでクローンを作成してReverse()しなくちゃいけない。

なので、クローンメソッドを追加。

    class AnalyzeData
    {
        public AnalyzeData Clone()
        {
            var clone = new AnalyzeData(Value);
            if (_analyzed)
            {
                clone.Analyzed();
            }
            return clone;
        }
    }
    class AnalyzeListData
    {
        public AnalyzeListData Clone()
        {
            var clone = new AnalyzeListData();
            clone.AnalyzeDatas = new List<AnalyzeData>();
            foreach (var data in AnalyzeDatas)
            {
                clone.AnalyzeDatas.Add(data.Clone());
            }
            if (_analyzed)
            {
                clone.Analyzed();
            }
            return clone;
        }
    }

そしてパターン6の実装。

データの順番に気をつけて。

        // 解析パターンその6
        // 端っこ(後ろ)が塗られていたら塗る。
        private void pattern6()
        {
            // Row
            pattern6Row();
            // Col
            pattern6Col();
        }

        private void pattern6Row()
        {
            int row = 0;
            foreach (var rowlist in rowNumbers)
            {
                if (rowlist.IsAnalyzed())
                {
                    row++;
                    continue;
                }
                var tempRowList = rowlist.Clone();
                tempRowList.AnalyzeDatas.Reverse();

                // 塗った場所が端っこならそこを塗る
                // マスクされていない部分をリスト化して取得する
                List<List<BitmapData>> data = new List<List<BitmapData>>();
                {
                    List<BitmapData> dataList = new List<BitmapData>();
                    for (int col = 0; col < colNumbers.Count; col++)
                    {
                        if (_bitmapData[row, col].IsMasked() == false)
                        {
                            dataList.Add(_bitmapData[row, col]);
                        }
                        else
                        {
                            if (dataList.Count != 0)
                            {
                                data.Add(dataList);
                                dataList = new List<BitmapData>();
                            }
                        }
                    }
                    if (dataList.Count != 0)
                    {
                        data.Add(dataList);
                    }

                    if (data.Count == 0)
                    {
                        row++;
                        continue;
                    }
                }

                int rowNumberIndex = 0;
                foreach (var dataList in data)
                {
                    // 端っこが塗られているか?
                    dataList.Reverse();
                    if (dataList[0].IsPainted())
                    {
                        // すでに処理済みか?
                        if (tempRowList.AnalyzeDatas[rowNumberIndex].IsAnalyzed())
                        {
                            rowNumberIndex++;
                            if (rowNumberIndex >= tempRowList.AnalyzeDatas.Count)
                            {
                                break;
                            }
                            continue;
                        }

                        // 数字に従ってマスを塗る
                        int count = 0;
                        foreach (var s in dataList)
                        {
                            if (count < tempRowList.AnalyzeDatas[rowNumberIndex].Value)
                            {
                                s.Paint();
                                count++;
                            }
                            else
                            {
                                if (s.IsMasked() == false)
                                {
                                    s.Mask();
                                }
                                tempRowList.AnalyzeDatas[rowNumberIndex].Analyzed();
                                tempRowList.CheckAnalyze();
                                break;
                            }
                        }
                    }
                    rowNumberIndex++;
                    if (rowNumberIndex >= tempRowList.AnalyzeDatas.Count)
                    {
                        break;
                    }
                }
                row++;
            }
        }

        private void pattern6Col()
        {
            int col = 0;
            foreach (var collist in colNumbers)
            {
                if (collist.IsAnalyzed())
                {
                    col++;
                    continue;
                }

                // 塗った場所が端っこならそこを塗る
                // マスクされていない部分をリスト化して取得する
                List<List<BitmapData>> data = new List<List<BitmapData>>();
                {
                    List<BitmapData> dataList = new List<BitmapData>();
                    for (int row = 0; row < rowNumbers.Count; row++)
                    {
                        if (_bitmapData[row, col].IsMasked() == false)
                        {
                            dataList.Add(_bitmapData[row, col]);
                        }
                        else
                        {
                            if (dataList.Count != 0)
                            {
                                data.Add(dataList);
                                dataList = new List<BitmapData>();
                            }
                        }
                    }
                    if (dataList.Count != 0)
                    {
                        data.Add(dataList);
                    }

                    if (data.Count == 0)
                    {
                        col++;
                        continue;
                    }
                }

                int colNumberIndex = 0;
                data.Reverse();
                foreach (var dataList in data)
                {
                    // 端っこが塗られているか?
                    dataList.Reverse();
                    if (dataList[0].IsPainted())
                    {
                        // すでに処理済みか?
                        if (collist.AnalyzeDatas[colNumberIndex].IsAnalyzed())
                        {
                            colNumberIndex++;
                            if (colNumberIndex >= collist.AnalyzeDatas.Count)
                            {
                                break;
                            }
                            continue;
                        }

                        // 数字に従ってマスを塗る
                        int count = 0;
                        foreach (var s in dataList)
                        {
                            if (count < collist.AnalyzeDatas[colNumberIndex].Value)
                            {
                                s.Paint();
                                count++;
                            }
                            else
                            {
                                if (s.IsMasked() == false)
                                {
                                    s.Mask();
                                }
                                collist.AnalyzeDatas[colNumberIndex].Analyzed();
                                collist.CheckAnalyze();
                                break;
                            }
                        }
                    }
                    colNumberIndex++;
                    if(colNumberIndex >= collist.AnalyzeDatas.Count)
                    {
                        break;
                    }
                }
                col++;
            }
        }

とりあえず、1回このロジックを当てはめてみる。

狙い通りに動いているみたいです。

なので、最後まで処理させてみる。

お、解けた。

多分、まだ完璧では無いですが、これで一歩前進です。

【C#】【ピクロス】【ALTSEED】解析パターンその5

前回までの状況はこちら。

最新ソースはこちら。(gitHub)

https://github.com/takishita2nd/Picross

さて、今回はここを攻めていきます。

ここはマスクされたマスのすぐ隣に塗られたマスがあるので、当てはめる数字が分かればここも確実に塗ることができます。

ただ、前から塗る場合と後ろから塗る場合で、一緒に考えるとハマってしまうので、今回は前から塗るケースを考えました。

つまり、今回の対象はここになります。

解き方はこんな風に考えました。

空いているマスを抽出し、その端っこ(今回は先頭)が塗られているならば、数字に従って塗る。

そして、その次のマスは必ずマスクされますので、マスクする。

これでいきます。

        // 解析パターンその5
        // 端っこ(先頭)が塗られていたら塗る。
        private void pattern5()
        {
            // Row
            pattern5Row();
            // Col
            pattern5Col();
        }

        private void pattern5Row()
        {
            int row = 0;
            foreach (var rowlist in rowNumbers)
            {
                if (rowlist.IsAnalyzed())
                {
                    row++;
                    continue;
                }
                rowlist.AnalyzeDatas.Reverse();

                // 塗った場所が端っこならそこを塗る
                // マスクされていない部分をリスト化して取得する
                List<List<BitmapData>> data = new List<List<BitmapData>>();
                {
                    List<BitmapData> dataList = new List<BitmapData>();
                    for (int col = 0; col < colNumbers.Count; col++)
                    {
                        if (_bitmapData[row, col].IsMasked() == false)
                        {
                            dataList.Add(_bitmapData[row, col]);
                        }
                        else
                        {
                            if (dataList.Count != 0)
                            {
                                data.Add(dataList);
                                dataList = new List<BitmapData>();
                            }
                        }
                    }
                    if (dataList.Count != 0)
                    {
                        data.Add(dataList);
                    }

                    if (data.Count == 0)
                    {
                        row++;
                        continue;
                    }
                }

                int rowNumberIndex = 0;
                foreach (var dataList in data)
                {
                    // 端っこが塗られているか?
                    if (dataList[0].IsPainted())
                    {
                        // すでに処理済みか?
                        if (rowlist.AnalyzeDatas[rowNumberIndex].IsAnalyzed())
                        {
                            rowNumberIndex++;
                            continue;
                        }

                        // 数字に従ってマスを塗る
                        int count = 0;
                        foreach (var s in dataList)
                        {
                            if (count < rowlist.AnalyzeDatas[rowNumberIndex].Value)
                            {
                                s.Paint();
                                count++;
                            }
                            else
                            {
                                if (s.IsMasked() == false)
                                {
                                    s.Mask();
                                }
                                rowlist.AnalyzeDatas[rowNumberIndex].Analyzed();
                                rowlist.CheckAnalyze();
                                break;
                            }
                        }
                    }
                }
                row++;
            }
        }

        private void pattern5Col()
        {
            int col = 0;
            foreach (var collist in colNumbers)
            {
                if (collist.IsAnalyzed())
                {
                    col++;
                    continue;
                }
                collist.AnalyzeDatas.Reverse();

                // 塗った場所が端っこならそこを塗る
                // マスクされていない部分をリスト化して取得する
                List<List<BitmapData>> data = new List<List<BitmapData>>();
                {
                    List<BitmapData> dataList = new List<BitmapData>();
                    for (int row = 0; row < rowNumbers.Count; row++)
                    {
                        if (_bitmapData[row, col].IsMasked() == false)
                        {
                            dataList.Add(_bitmapData[row, col]);
                        }
                        else
                        {
                            if (dataList.Count != 0)
                            {
                                data.Add(dataList);
                                dataList = new List<BitmapData>();
                            }
                        }
                    }
                    if (dataList.Count != 0)
                    {
                        data.Add(dataList);
                    }

                    if (data.Count == 0)
                    {
                        col++;
                        continue;
                    }
                }

                int colNumberIndex = 0;
                foreach (var dataList in data)
                {
                    // 端っこが塗られているか?
                    if (dataList[0].IsPainted())
                    {
                        // すでに処理済みか?
                        if (collist.AnalyzeDatas[colNumberIndex].IsAnalyzed())
                        {
                            colNumberIndex++;
                            continue;
                        }

                        // 数字に従ってマスを塗る
                        int count = 0;
                        foreach (var s in dataList)
                        {
                            if (count < collist.AnalyzeDatas[colNumberIndex].Value)
                            {
                                s.Paint();
                                count++;
                            }
                            else
                            {
                                if (s.IsMasked() == false)
                                {
                                    s.Mask();
                                }
                                collist.AnalyzeDatas[colNumberIndex].Analyzed();
                                collist.CheckAnalyze();
                                break;
                            }
                        }
                    }
                }
                col++;
            }
        }

考え方は簡単に思えますが、実際に実装して見ると結構複雑になったりします。

このコーディングが正しく動かすまでに何度もデバッグを繰り返しました。

プログラミングとはそういうもんです。

基本的にトライエラーで正しく動くまで修正するのです。

実行結果はこうなりました。

小さな一歩ですが、確実に行きましょう。

【C#】【ピクロス】【ALTSEED】解析パターンその4

前回までの状況はこちら。

最新ソースはこちら。(gitHub)

https://github.com/takishita2nd/Picross

この問題を解いてみました。

とりあえず、前回までのロジックを実装した解析プログラムを適用してみます。

当然、解けませんでした。

では、ここに注目してみましょう。

左の数字を見ると、真ん中に4マス空いていますので、そこが塗れると思います。

逆に考えれば、その左右にある1マスの所には塗れないことが明らかになっています。

ピクロスの解析の基本は、塗れないところはマスクしていく、と言うことなので、今回の解析パターンでは、こういったスペースをマスクするロジックを作成していきます。

それができれば、残ったスペースには解析パターン2が適用されて塗ってくれるはずです。

そこで、こういうクラスを用意しました。

        class Pattern4Data
        {
            public int Index;
            public int Value;
            public Pattern4Data(int index, int value)
            {
                Index = index;
                Value = value;
            }
        }

空きがある位置と、空いているマスの数を数えて保持します。

これをListで持ちます。

List<Pattern4Data> countList = new List<Pattern4Data>();

こうやって、空白のある部分を全て抽出します。

その中から、与えられた数字のマスが確実に入らないところ、とりあえずは、一番小さな値よりも小さいマスを検出して、その空白を全てマスクして塗れない様にします。

と言うわけで、こういうコードを書きました。

        // 解析パターンその4
        // 空いているマスが少なくて塗れないマスをマスクする
        private void pattern4()
        {
            // Row
            pattern4Row();
            // Col
            pattern4Col();
        }

        private void pattern4Row()
        {
            int row = 0;
            foreach (var rowlist in rowNumbers)
            {
                if (rowlist.IsAnalyzed())
                {
                    row++;
                    continue;
                }

                // マスクされていない連続したマスを数える
                int col = 0;
                int rememberCol = colNumbers.Count;
                int count = 0;
                List<Pattern4Data> countList = new List<Pattern4Data>();
                while (col < colNumbers.Count)
                {
                    if (_bitmapData[row, col].IsMasked() == false)
                    {
                        count++;
                        if(rememberCol == colNumbers.Count)
                        {
                            rememberCol = col;
                        }
                    }
                    else
                    {
                        if (count != 0)
                        {
                            countList.Add(new Pattern4Data(rememberCol, count));
                            count = 0;
                            rememberCol = colNumbers.Count;
                        }
                    }
                    col++;
                    if (col == colNumbers.Count)
                    {
                        if (count != 0)
                        {
                            countList.Add(new Pattern4Data(rememberCol, count));
                        }
                    }
                }

                // 数字の中で一番小さい値を取得する
                int val = colNumbers.Count;
                foreach(var s in rowlist.AnalyzeDatas)
                {
                    if(val > s.Value)
                    {
                        val = s.Value;
                    }
                }

                // マスクされていない連続したマスの数が数字より小さい場合はマスクする
                foreach(var data in countList)
                {
                    if(val > data.Value)
                    {
                        for(int i = 0; i< data.Value; i++)
                        {
                            if(_bitmapData[row, data.Index + i].IsValid() == false)
                            {
                                _bitmapData[row, data.Index + i].Mask();
                            }
                        }
                    }
                }
                row++;
            }
        }

        private void pattern4Col()
        {
            int col = 0;
            foreach (var collist in colNumbers)
            {
                if (collist.IsAnalyzed())
                {
                    col++;
                    continue;
                }

                // マスクされていない連続したマスを数える
                int row = 0;
                int rememberRow = rowNumbers.Count;
                int count = 0;
                List<Pattern4Data> countList = new List<Pattern4Data>();
                while (row < rowNumbers.Count)
                {
                    if (_bitmapData[row, col].IsMasked() == false)
                    {
                        count++;
                        if(rememberRow == rowNumbers.Count)
                        {
                            rememberRow = row;
                        }
                    }
                    else
                    {
                        if (count != 0)
                        {
                            countList.Add(new Pattern4Data(rememberRow, count));
                            count = 0;
                            rememberRow = rowNumbers.Count;
                        }
                    }
                    row++;
                    if (row == rowNumbers.Count)
                    {
                        if (count != 0)
                        {
                            countList.Add(new Pattern4Data(rememberRow, count));
                        }
                    }
                }

                // 数字の中で一番小さい値を取得する
                int val = rowNumbers.Count;
                foreach(var s in collist.AnalyzeDatas)
                {
                    if(val > s.Value)
                    {
                        val = s.Value;
                    }
                }

                // マスクされていない連続したマスの数が数字より小さい場合はマスクする
                foreach(var data in countList)
                {
                    if(val > data.Value)
                    {
                        for(int i = 0; i< data.Value; i++)
                        {
                            if(_bitmapData[data.Index + i, col].IsValid() == false)
                            {
                                _bitmapData[data.Index + i, col].Mask();
                            }
                        }
                    }
                }
                col++;
            }
        }
        public BitmapData[,] Run()
        {
            pattern1();
            while (checkPainedCount())
            {
                pattern2();
                pattern3();
                pattern4();
            }
            return _bitmapData;
        }

実行結果はこうなりました。

想定通りの動作をしていますね。

【C#】【ピクロス】【ALTSEED】解析パターンその3

前回までの状況はこちら。

最新ソースはこちら。(gitHub)

https://github.com/takishita2nd/Picross

次は、ここを攻めていきます。

この赤い部分、すでに塗られているマスから、開いているマスは全て塗ることができないことが分かると思います。

これを処理していきましょう。

パターン2では塗られていないマスを数えましたが、パターン3では塗られているマスを数えます。

コードはパターン2と似ていますが、条件が変わっています。

        // 解析パターンその3
        // すでに塗りつぶされたマスからマスクをする
        private void pattern3()
        {
            // Row
            pattern3Row();
            // Col
            pattern3Col();
        }

        private void pattern3Row()
        {
            int row = 0;
            foreach (var rowlist in rowNumbers)
            {
                if (rowlist.IsAnalyzed())
                {
                    row++;
                    continue;
                }

                // 塗ったマスを数える
                int col = 0;
                int count = 0;
                List<int> countList = new List<int>();
                while (col < colNumbers.Count)
                {
                    if (_bitmapData[row, col].IsPainted())
                    {
                        count++;
                    }
                    else
                    {
                        if (count != 0)
                        {
                            countList.Add(count);
                            count = 0;
                        }
                    }
                    col++;
                    if (col == colNumbers.Count)
                    {
                        if (count != 0)
                        {
                            countList.Add(count);
                        }
                    }
                }
                // 数えた数字が全て一致すれば確定とする
                bool result = true;
                if (rowlist.AnalyzeDatas.Count != countList.Count)
                {
                    row++;
                    continue;
                }
                for (int i = 0; i < countList.Count; i++)
                {
                    if (rowlist.AnalyzeDatas[i].Value != countList[i])
                    {
                        result = false;
                    }
                }
                if (result)
                {
                    // 開いているところをマスクする
                    col = 0;
                    while (col < colNumbers.Count)
                    {
                        if (_bitmapData[row, col].IsValid() == false)
                        {
                            _bitmapData[row, col].Mask();
                        }
                        col++;
                    }
                    rowlist.Analyzed();
                }
                row++;
            }
        }

        private void pattern3Col()
        {
            int col = 0;
            foreach (var collist in colNumbers)
            {
                if (collist.IsAnalyzed())
                {
                    col++;
                    continue;
                }

                // 塗ったマスを数える
                int row = 0;
                int count = 0;
                List<int> countList = new List<int>();
                while (row < rowNumbers.Count)
                {
                    if (_bitmapData[row, col].IsPainted())
                    {
                        count++;
                    }
                    else
                    {
                        if (count != 0)
                        {
                            countList.Add(count);
                            count = 0;
                        }
                    }
                    row++;
                    if (row == rowNumbers.Count)
                    {
                        if(count != 0)
                        {
                            countList.Add(count);
                        }
                    }
                }
                // 数えた数字が全て一致すれば確定とする
                bool result = true;
                if (collist.AnalyzeDatas.Count != countList.Count)
                {
                    col++;
                    continue;
                }
                countList.Reverse();
                for (int i = 0; i < countList.Count; i++)
                {
                    if (collist.AnalyzeDatas[i].Value != countList[i])
                    {
                        result = false;
                    }
                }
                if (result)
                {
                    // 開いているところをマスクする
                    row = 0;
                    while (row < rowNumbers.Count)
                    {
                        if (_bitmapData[row, col].IsValid() == false)
                        {
                            _bitmapData[row, col].Mask();
                        }
                        row++;
                    }
                    collist.Analyzed();
                }
                col++;
            }
        }

実行結果はこちら。

ここまで来たら、パターン2とパターン3の繰り返しで全て塗れるのでは無いでしょうか?

では、パターン2とパターン3をループ処理させるのに、終了条件を判定する処理を作成します。

        private bool checkPainedCount()
        {
            int newPaintedCount = 0;
            for (int row = 0; row < rowNumbers.Count; row++)
            {
                for(int col = 0; col < colNumbers.Count; col++)
                {
                    if(_bitmapData[row, col].IsValid())
                    {
                        newPaintedCount++;
                    }
                }
            }
            if(oldPaintedCount == newPaintedCount)
            {
                return false;
            }
            oldPaintedCount = newPaintedCount;
            return true;
        }

考え方は、全てのマスが確定したかを判定し、その数を数えます。

ループ前とループ後のカウント数を比較し、変化があればカウント数を更新、変化が無ければループを終了します。

        public BitmapData[,] Run()
        {
            pattern1();
            while (checkPainedCount())
            {
                pattern2();
                pattern3();
            }
            return _bitmapData;
        }

実行結果はこうなりました。

これは・・・黒電話かな?

解けたようです。

まだ完全ではないですが、簡単な問題ならこれで解けそうです。

【C#】【ピクロス】【ALTSEED】解析パターンその2

前回までの状況はこちら。

最新ソースはこちら。(gitHub)

https://github.com/takishita2nd/Picross

つぎはどう攻めていくかというと、ここに注目しましょう。

ここも塗れるのが確定していますよね。

ここを塗るための条件を確認しましょう。

マスクされていないマスを数え、連続したマスの数と、範囲の数が全て一致した場合、確定とする。

つまり、

マスクされていないマスが2個、1個、2個と並んでいて、これらが全て左の数字と一致するため、この開いているマスは全て塗ることができます。

これをコーディングします。

    // 解析パターンその2
    private void pattern2()
    {
        // Row
        pattern2Row();
        // Col
        pattern2Col();
    }
        private void pattern2Row()
        {
            int row = 0;
            foreach (var rowlist in rowNumbers)
            {
                if (rowlist.IsAnalyzed())
                {
                    row++;
                    continue;
                }

                // マスクされていない連続したマスを数える
                int col = 0;
                int count = 0;
                List<int> countList = new List<int>();
                while (col < colNumbers.Count)
                {
                    if (_bitmapData[row, col].IsMasked() == false)
                    {
                        count++;
                    }
                    else
                    {
                        if (count != 0)
                        {
                            countList.Add(count);
                            count = 0;
                        }
                    }
                    col++;
                    if(col == colNumbers.Count)
                    {
                        countList.Add(count);
                    }
                }
                // 数えた数字が全て一致すれば確定とする
                bool result = true;
                if (rowlist.AnalyzeDatas.Count != countList.Count)
                {
                    row++;
                    continue;
                }
                for (int i = 0; i < countList.Count; i++)
                {
                    if (rowlist.AnalyzeDatas[i].Value != countList[i])
                    {
                        result = false;
                    }
                }
                if (result)
                {
                    // 開いているところを塗る
                    col = 0;
                    while (col < colNumbers.Count)
                    {
                        if(_bitmapData[row, col].IsValid() == false)
                        {
                            _bitmapData[row, col].Paint();
                        }
                        col++;
                    }
                    rowlist.Analyzed();
                }
                row++;
            }
        }

        private void pattern2Col()
        {
            int col = 0;
            foreach (var collist in colNumbers)
            {
                if (collist.IsAnalyzed())
                {
                    col++;
                    continue;
                }

                // マスクされていない連続したマスを数える
                int row = 0;
                int count = 0;
                List<int> countList = new List<int>();
                while (row < rowNumbers.Count)
                {
                    if (_bitmapData[row, col].IsMasked() == false)
                    {
                        count++;
                    }
                    else
                    {
                        if (count != 0)
                        {
                            countList.Add(count);
                            count = 0;
                        }
                    }
                    row++;
                    if (row == rowNumbers.Count)
                    {
                        countList.Add(count);
                    }
                }
                // 数えた数字が全て一致すれば確定とする
                bool result = true;
                if (collist.AnalyzeDatas.Count != countList.Count)
                {
                    col++;
                    continue;
                }
                countList.Reverse();
                for (int i = 0; i < countList.Count; i++)
                {
                    if (collist.AnalyzeDatas[i].Value != countList[i])
                    {
                        result = false;
                    }
                }
                if (result)
                {
                    // 開いているところを塗る
                    row = 0;
                    while (row < rowNumbers.Count)
                    {
                        if (_bitmapData[row, col].IsValid() == false)
                        {
                            _bitmapData[row, col].Paint();
                        }
                        row++;
                    }
                    collist.Analyzed();
                }
                col++;
            }
        }

数える→数を確認する→塗るの順で処理しています。

少しデータの持ち方も変えています。

    private List<AnalyzeListData> rowNumbers;
    private List<AnalyzeListData> colNumbers;
    class AnalyzeListData
    {
        private bool _analyzed;
        public List<AnalyzeData> AnalyzeDatas;

        public AnalyzeListData()
        {
            _analyzed = false;
            AnalyzeDatas = new List<AnalyzeData>();
        }

        public bool IsAnalyzed()
        {
            return _analyzed;
        }

        public void Analyzed()
        {
            _analyzed = true;
        }
    class AnalyzeData
    {
        private bool _analyzed;
        public int Value { get; }

        public AnalyzeData(int value)
        {
            Value = value;
            _analyzed = false;
        }

        public bool IsAnalyzed()
        {
            return _analyzed;
        }

        public void Analyzed()
        {
            _analyzed = true;
        }

    }

どのデータを塗ったかどうかを確認できるようにしています。

実行結果はこうなりました。

うん、順調ですね。

【C#】【ピクロス】【ALTSEED】解析パターンその1

前回までの状況はこちら。

最新ソースはこちら。(gitHub)

https://github.com/takishita2nd/Picross

さぁ、解析処理を作っていくぞ。

例題として、こちらの問題を用意しました。

データを入力。

まず、ピクロス解法の定石パターンとして、確実に塗れるところから塗っていくというもの。

この例題からすると、

この赤い部分は全て塗れるのは、すぐに分かりますよね。

さらに、

ここも塗れるのは分かりますでしょうか。

例えば、[1,4,3]と合った場合は、

■×■■■■×■■■

と10マスが確定しますよね?

これを公式化すると、

数字の合計+(数字の数ー1)=マスの数

※1+4+3+(3-1) = 10

となった場合、その列または行の塗れるマスは自動的に確定します。

これを実装します。

        // 解析パターンその1
        private void pattern1()
        {
            // Row
            pattern1Row();
            // Col
            pattern1Col();
        }

        /**
         * Rowに対して解析パターン1を適用
         */
        private void pattern1Row()
        {
            int row = 0;
            foreach (var rowlist in rowNumbers)
            {
                int total = 0;
                foreach (var v in rowlist)
                {
                    total += v;
                }
                total += rowlist.Count - 1;
                if (total == colNumbers.Count)
                {
                    // 塗れるマス確定
                    // リストを反転コピー
                    List<int> revRowList = new List<int>();
                    foreach (var v in rowlist)
                    {
                        revRowList.Add(v);
                    }
                    revRowList.Reverse();

                    int col = 0;
                    foreach (var v in revRowList)
                    {
                        int c;
                        for (c = 0; c < v; c++)
                        {
                            _bitmapData[row, col + c].Paint();
                        }
                        if(col + c < colNumbers.Count)
                        {
                            _bitmapData[row, col + c].Mask();
                            c++;
                        }
                        col += c;
                    }
                }
                row++;
            }
        }

        /**
         * Colに対して解析パターン1を適用
         */
        private void pattern1Col()
        {
            int col = 0;
            foreach (var collist in colNumbers)
            {
                int total = 0;
                foreach (var v in collist)
                {
                    total += v;
                }
                total += collist.Count - 1;
                if (total == colNumbers.Count)
                {
                    // 塗れるマス確定
                    // リストを反転コピー
                    List<int> revColList = new List<int>();
                    foreach (var v in collist)
                    {
                        revColList.Add(v);
                    }
                    revColList.Reverse();

                    int row = 0;
                    foreach (var v in revColList)
                    {
                        int r;
                        for (r = 0; r < v; r++)
                        {
                            _bitmapData[row + r, col].Paint();
                        }
                        if (row + r < rowNumbers.Count)
                        {
                            _bitmapData[row + r, col].Mask();
                            r++;
                        }
                        row += r;
                    }
                }
                col++;
            }
        }

列の数字データは右から順に並んでいるので、これを反転させる必要があります。

ただ、直接Reverse()を使用すると、インプットデータが壊れてしまうので、コピーを作成してからReverse()を使用します。

あとは、Listの先頭からピクロスのルールに従って塗っていきます。

実行結果はこうなりました。

解析がしやすくなるように、塗れないマスは×を表示するようにしました。

まぁ、ここまでは順調ですな。