「#ピクロス」タグアーカイブ

【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の先頭からピクロスのルールに従って塗っていきます。

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

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

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

【C#】【ピクロス】【ALTSEED】解析結果をUIに反映させる

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

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

https://github.com/takishita2nd/Picross

実際に解析を行うクラスを作成していきます。

まずは解析中の状態を保持するクラスを定義します。

class BitmapData
{
    public int Row { get; }
    public int Col { get; }
    // 塗るで確定
    private bool _isPaint;
    // 塗らないで確定
    private bool _isFilter;

    public BitmapData(int row, int col)
    {
        Row = row;
        Col = col;
        _isPaint = false;
        _isFilter = false;
    }

    public bool IsPainted()
    {
        return _isPaint;
    }

    public bool IsFilted()
    {
        return _isFilter;
    }

    public bool IsValid()
    {
        if(_isPaint | _isFilter)
        {
            return true;
        }
        else
        {
            return false;
        }
    }
}

多分、解析に必要なデータは、塗ったマスか、塗ることができないマスか、まだ確定していないマスか、の三つが必要になると思います。

なので、それらの状態を保持し返す事ができるメソッドを一通り用意してみました。

とりあえず、これでいいや。

必要になったら、その都度追加していく。

これを使用します。

class PicrossAnalyze
{
    List<List<int>> rowNumbers;
    List<List<int>> cowNumbers;
    BitmapData[,] _bitmapData;

    public PicrossAnalyze(List<List<NumberSquare>> rowNumberSquares, List<List<NumberSquare>> colNumberSquares)
    {
        _bitmapData = new BitmapData[rowNumberSquares.Count, colNumberSquares.Count];
        for(int r = 0; r < rowNumberSquares.Count; r++)
        {
            for(int c = 0; c < colNumberSquares.Count; c++)
            {
                _bitmapData[r, c] = new BitmapData(r, c);
            }
        }

        rowNumbers = new List<List<int>>();
        foreach(var rowList in rowNumberSquares)
        {
            List<int> list = new List<int>();
            foreach(var s in rowList)
            {
                list.Add(s.GetValue());
            }
            rowNumbers.Add(list);
        }

        cowNumbers = new List<List<int>>();
        foreach (var colList in colNumberSquares)
        {
            List<int> list = new List<int>();
            foreach (var s in colList)
            {
                list.Add(s.GetValue());
            }
            cowNumbers.Add(list);
        }
    }

    public bool[,] Run()
    {
        // モック
        bool[,] ret = new bool[10, 10] {
            {true, false, true, false, true, true, false, true, false, true },
            {true, false, true, false, true, true, false, true, false, true },
            {true, false, true, false, true, true, false, true, false, true },
            {true, false, true, false, true, true, false, true, false, true },
            {true, false, true, false, true, true, false, true, false, true },
            {true, false, true, false, true, true, false, true, false, true },
            {true, false, true, false, true, true, false, true, false, true },
            {true, false, true, false, true, true, false, true, false, true },
            {true, false, true, false, true, true, false, true, false, true },
            {true, false, true, false, true, true, false, true, false, true }
        };

        return ret;
    }
}

コンストラクタで必要なデータを渡し、必要なデータを取り出します。

とりあえず、この処理はここまで。

先に解析結果をUIに反映させる処理を書きましょう。

表示するデータは、とりあえず固定値を返すモックにしておきます。

これをUI側に実装します。

        anlyzeButton.SetAction(() =>
        {
            PicrossAnalyze picross = new PicrossAnalyze(rowNumberSquare, colNumberSquare);
            bool[,] ret = picross.Run();
            for(int row = 0; row < rowNumberSquare.Count; row++)
            {
                for(int col = 0; col < colNumberSquare.Count; col++)
                {
                    if(ret[row,col] == true)
                    {
                        drawSquares[row][col].Paint();
                    }
                }
            }
        });

実行結果はこんな感じになります。

解析状況を可視化しなければ、解析処理を作成するのに困るので、先にこちらの処理を作成した、と言うわけです。

次回は実際に問題データを入力して、簡単なロジックから作成していきましょう。

【C#】【ピクロス】【ALTSEED】セーブ、ロード機能を追加

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

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

https://github.com/takishita2nd/Picross

前回、汚いUIをどうしようか、ということを書きましたが、

見た目は後回し、という方針で行こうと思います。

さっさとUIを作って、解析ロジックに取りかかりたい、というのが本音です。

今回は、デバッグで必要になるであろう、セーブ、ロード機能を作成していきます。

デバッグの度に数字を入力するの、めんどくさいんで。

まずは、ボタン配置。

            // 解析ボタン
            var anlyzeButton = new Button(10, 50, "解析");
            anlyzeButton.Show();
            asd.Engine.AddObject2D(anlyzeButton.getBackTexture());
            asd.Engine.AddObject2D(anlyzeButton.getTextObject());
            buttons.Add(anlyzeButton);
            anlyzeButton.SetAction(() =>
            {
            });

            // セーブボタン
            var saveButton = new Button(10, 90, "セーブ");
            saveButton.Show();
            asd.Engine.AddObject2D(saveButton.getBackTexture());
            asd.Engine.AddObject2D(saveButton.getTextObject());
            buttons.Add(saveButton);
            saveButton.SetAction(() =>
            {
                FileAccess.Save(rowNumberSquare, colNumberSquare);
            });

            // ロードボタン
            var loadButton = new Button(10, 130, "ロード");
            loadButton.Show();
            asd.Engine.AddObject2D(loadButton.getBackTexture());
            asd.Engine.AddObject2D(loadButton.getTextObject());
            buttons.Add(loadButton);
            loadButton.SetAction(() =>
            {
                FileAccess.Load(ref rowNumberSquare, ref colNumberSquare);
            });

入力データを保存、読み取りするコードを書いていきます。

[JsonObject("dataModel")]
class Data
{
    [JsonProperty("rowdata")]
    public List<List<int>> RowData { get; set; }
    [JsonProperty("coldata")]
    public List<List<int>> ColData { get; set; }
}
class FileAccess
{
    private const string _filename = "save.dat";

    public static void Save(List<List<NumberSquare>> row, List<List<NumberSquare>> col)
    {
        if (File.Exists(_filename) == true)
        {
            File.Delete(_filename);
        }
        Data data = new Data();
        data.RowData = new List<List<int>>();
        foreach(var r in row)
        {
            List<int> list = new List<int>();
            foreach(var s in r)
            {
                if(s.GetValue() != 0)
                {
                    list.Add(s.GetValue());
                }
            }
            data.RowData.Add(list);
        }
        data.ColData = new List<List<int>>();
        foreach (var c in col)
        {
            List<int> list = new List<int>();
            foreach (var s in c)
            {
                if (s.GetValue() != 0)
                {
                    list.Add(s.GetValue());
                }
            }
            data.ColData.Add(list);
        }
        var str = JsonConvert.SerializeObject(data);
        using (var stream = new StreamWriter(_filename, true))
        {
            stream.Write(str);
        }
    }

    public static void Load(ref List<List<NumberSquare>> rowList, ref List<List<NumberSquare>> colList)
    {
        foreach(var r in rowList)
        {
            foreach(var s in r)
            {
                asd.Engine.RemoveObject2D(s.getBackTexture());
                asd.Engine.RemoveObject2D(s.getTextObject());
            }
        }
        foreach (var c in colList)
        {
            foreach (var s in c)
            {
                asd.Engine.RemoveObject2D(s.getBackTexture());
                asd.Engine.RemoveObject2D(s.getTextObject());
            }
        }

        string str = string.Empty;
        using (var stream = new StreamReader(_filename, true))
        {
            str = stream.ReadToEnd();
        }
        var data = JsonConvert.DeserializeObject<Data>(str);
        rowList = new List<List<NumberSquare>>();
        {
            int row = 0;
            foreach (var r in data.RowData)
            {
                List<NumberSquare> list = new List<NumberSquare>();
                int col = -1;
                foreach (var v in r)
                {
                    var s = new NumberSquare(row, col);
                    s.SetValue(v.ToString());
                    asd.Engine.AddObject2D(s.getBackTexture());
                    asd.Engine.AddObject2D(s.getTextObject());
                    list.Add(s);
                    col--;
                }
                {
                    var s = new NumberSquare(row, col);
                    s.SetValue("0");
                    asd.Engine.AddObject2D(s.getBackTexture());
                    asd.Engine.AddObject2D(s.getTextObject());
                    list.Add(s);
                }
                rowList.Add(list);
                row++;
            }
        }
        colList = new List<List<NumberSquare>>();
        {
            int col = 0;
            foreach (var c in data.ColData)
            {
                List<NumberSquare> list = new List<NumberSquare>();
                int row = -1;
                foreach (var v in c)
                {
                    var s = new NumberSquare(row, col);
                    s.SetValue(v.ToString());
                    asd.Engine.AddObject2D(s.getBackTexture());
                    asd.Engine.AddObject2D(s.getTextObject());
                    list.Add(s);
                    row--;
                }
                {
                    var s = new NumberSquare(row, col);
                    s.SetValue("0");
                    asd.Engine.AddObject2D(s.getBackTexture());
                    asd.Engine.AddObject2D(s.getTextObject());
                    list.Add(s);
                }
                colList.Add(list);
                col++;
            }
        }
    }
}

保存処理は、入力したデータをJsonに変換して、保存させます。

ロード処理は、一旦数字入力マスを全て削除し、管理データを作り直します。

C#ってrefを付けないとオブジェクトの参照渡しできないんですね。

まだまだ勉強しなくちゃいけないことが多いなぁ。

まだ問題とか、不具合あるかもしれないけど、それはその都度修正していくきます。

この昨日は完成後に削除する予定なので。

さて、次回からいよいよロジックに着手できるー!

【C#】【ピクロス】【ALTSEED】数字入力マスに数字を入力する

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

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

https://github.com/takishita2nd/Picross

前回追加した数字入力マスに数字を入力する処理を作っていきます。

数字入力はサイズ変更ダイアログと同じパレットを使用します。

            // パレット
            Palette palette = new Palette();
            palette.SetEngine();
            palette.Hide();

数字入力マスをクリックすると、パレットを表示させます。

パレット表示中は、マウスカーソルの所にある数字マスのテクスチャを色つきに変更します。

そして、数字をクリックすると、その数字が数字マスに入ります。

パレットの外をクリックすると、パレットを閉じ、入力終了になります。

これをコードに書いたらこうなりました。

            while (asd.Engine.DoEvents())
            {
                asd.Vector2DF pos = asd.Engine.Mouse.Position;
                if (!dialog.IsShow())
                {
                    if (!palette.IsShow())
                    {
                        foreach (var rowList in rowNumberSquare)
                        {
                            foreach (var s in rowList)
                            {
                                s.UpdateTexture(pos);
                            }
                        }
                        foreach (var colList in colNumberSquare)
                        {
                            foreach (var s in colList)
                            {
                                s.UpdateTexture(pos);
                            }
                        }
                        foreach (Button button in buttons)
                        {
                            button.UpdateTexture(pos);
                        }
                    }
                    else
                    {
                        palette.UpdateTexture(pos);
                    }
                }
                else
                {
                    dialog.UpdateTexture(pos);
                }

                if (asd.Engine.Mouse.LeftButton.ButtonState == asd.ButtonState.Push)
                {
                    if (!dialog.IsShow())
                    {
                        if (!palette.IsShow())
                        {
                            int rowIndex;
                            int colIndex;
                            rowIndex = 0;
                            foreach (var rowList in rowNumberSquare)
                            {
                                colIndex = -1;
                                foreach (var s in rowList)
                                {
                                    if (s.IsClick(pos))
                                    {
                                        selectedNumberSquare = s;
                                        selectedRowIndex = rowIndex;
                                        selectedColIndex = colIndex;
                                        palette.Show(pos);
                                    }
                                    colIndex--;
                                }
                                rowIndex++;
                            }
                            colIndex = 0;
                            foreach (var colList in colNumberSquare)
                            {
                                rowIndex = -1;
                                foreach (var s in colList)
                                {
                                    if (s.IsClick(pos))
                                    {
                                        selectedNumberSquare = s;
                                        selectedRowIndex = rowIndex;
                                        selectedColIndex = colIndex;
                                        palette.Show(pos);
                                    }
                                    rowIndex--;
                                }
                                colIndex++;
                            }
                            foreach (Button button in buttons)
                            {
                                if (button.IsClick(pos))
                                {
                                    button.OnClick();
                                }
                            }
                        }
                        else
                        {
                            if (palette.IsClick(pos))
                            {
                                string v = palette.GetClickValue(pos);
                                updateTextValue(selectedNumberSquare, v);
                            }
                            else
                            {
                                palette.Hide();
                                if(selectedNumberSquare != null)
                                {
                                    if(selectedNumberSquare.GetStringValue() != string.Empty)
                                    {
                                        if (selectedRowIndex >= 0)
                                        {
                                            var square = new NumberSquare(selectedRowIndex, selectedColIndex - 1);
                                            asd.Engine.AddObject2D(square.getBackTexture());
                                            asd.Engine.AddObject2D(square.getTextObject());
                                            rowNumberSquare[selectedRowIndex].Add(square);
                                        }
                                        else if (selectedColIndex >= 0)
                                        {
                                            var square = new NumberSquare(selectedRowIndex - 1, selectedColIndex);
                                            asd.Engine.AddObject2D(square.getBackTexture());
                                            asd.Engine.AddObject2D(square.getTextObject());
                                            colNumberSquare[selectedColIndex].Add(square);
                                        }
                                    }
                                }
                            }
                        }
                    }
                    else
                    {
                        dialog.OnClick(pos);
                    }
                }

実行結果はこんな感じになります。

数字入力を終えると、さらに入力スペースが増えるように細工しています。

ただ、このあたりの処理は変更するかもしれません。

ここどうするのが良いのかなーと言うのを未だに考えています。

【C#】【ピクロス】【ALTSEED】数字入力マスを追加する。

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

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

https://github.com/takishita2nd/Picross

ピクロス解析のヒントとなる数字の入力マスを作成していきます。

イメージとしては、マウスカーソルが上にあると色が変化し、クリックするとパレットを表示する、という感じです。

まずはマスを作成しましょう。

数字を入力するマスのクラスを作成します。

class NumberSquare : ObjectBase
{
    protected int _row;
    protected int _col;
    protected const int setPositionX = 200;
    protected const int setPositionY = 200;
    protected const int fontOffsetX = 19;
    protected const int fontOffsetY = 9;
    private string _value;

    public NumberSquare(int row, int col)
    {
        width = 32;
        height = 32;
        _value = string.Empty;
        _row = row;
        _col = col;
        _x = col * width + setPositionX;
        _y = row * height + setPositionY;

        _backTexture = new asd.TextureObject2D();
        _backTexture.Texture = Resource.GetPicrossTexture();
        _backTexture.Position = new asd.Vector2DF(_x, _y);

        _valueText = new asd.TextObject2D();
        _valueText.Font = Resource.getFont();
        _valueText.Position = new asd.Vector2DF(_x + fontOffsetX, _y + fontOffsetY);
    }

    public void SetValue(string value)
    {
        _value = value;
        _valueText.Text = _value;
    }

    public void UpdateTexture(asd.Vector2DF pos)
    {
        if (pos.X > _x && pos.X < _x + width
            && pos.Y > _y && pos.Y < _y + height)
        {
            _backTexture.Texture = Resource.GetSelectedNumberTexture();
        }
        else
        {
            _backTexture.Texture = Resource.GetPicrossTexture();
        }
    }

    public int getValue()
    {
        return int.Parse(_value);
    }
}

これをUIに配置します。

マスのオブジェクトは

List<List<NumberSquare>>

の形で保持します。

一番外のList<>はROWまたはCOLのサイズ分だけ持ち、List<List<>>は入力した数字の数だけ保持します。

これをROW、COLの二つ持ちます。

    private List<List<NumberSquare>> rowNumberSquare = new List<List<NumberSquare>>();
    private List<List<NumberSquare>> colNumberSquare = new List<List<NumberSquare>>();

配置。

        // 数字の入力するマス
        for(int row = 0; row < 10; row++)
        {
            NumberSquare square = new NumberSquare(row, -1);
            asd.Engine.AddObject2D(square.getBackTexture());
            asd.Engine.AddObject2D(square.getTextObject());
            List<NumberSquare> rowList = new List<NumberSquare>();
            rowList.Add(square);
            rowNumberSquare.Add(rowList);
        }
        for (int col = 0; col < 10; col++)
        {
            NumberSquare square = new NumberSquare(-1, col);
            asd.Engine.AddObject2D(square.getBackTexture());
            asd.Engine.AddObject2D(square.getTextObject());
            List<NumberSquare> colList = new List<NumberSquare>();
            colList.Add(square);
            colNumberSquare.Add(colList);
        }

マウスがマスの上にきたときの処理。

        while (asd.Engine.DoEvents())
        {
            asd.Vector2DF pos = asd.Engine.Mouse.Position;
            if(!dialog.IsShow())
            {
                foreach (var rowList in rowNumberSquare)
                {
                    foreach (var s in rowList)
                    {
                        s.UpdateTexture(pos);
                    }
                }
                foreach (var colList in colNumberSquare)
                {
                    foreach (var s in colList)
                    {
                        s.UpdateTexture(pos);
                    }
                }
                foreach (Button button in buttons)
                {
                    button.UpdateTexture(pos);
                }
            }

そして、忘れてはいけないのが、サイズ変更を行ったとき。

サイズ変更を行ったら、それに合わせてListの数も変更しなければいけません。

        dialog.SetAction(() =>
        {
            dialog.Hide();
            int row = dialog.GetRowValue();
            int col = dialog.GetColValue();
            while(row != drawSquares.Count)
            {
                if(drawSquares.Count > row)
                {
                    {
                        // マスの削除
                        var rowList = drawSquares[drawSquares.Count - 1];
                        foreach (var c in rowList)
                        {
                            asd.Engine.RemoveObject2D(c.getBackTexture());
                        }
                        drawSquares.RemoveAt(drawSquares.Count - 1);
                    }

                    {
                        // 数字のマスの削除
                        var rowList = rowNumberSquare[rowNumberSquare.Count - 1];
                        foreach (var s in rowList)
                        {
                            asd.Engine.RemoveObject2D(s.getBackTexture());
                            asd.Engine.RemoveObject2D(s.getTextObject());
                        }
                        rowNumberSquare.RemoveAt(rowNumberSquare.Count - 1);
                    }
                }
                else
                {
                    {
                        // マスの追加
                        List<DrawSquare> rowList = new List<DrawSquare>();
                        for (int c = 0; c < drawSquares[0].Count; c++)
                        {
                            var square = new DrawSquare(drawSquares.Count, c);
                            asd.Engine.AddObject2D(square.getBackTexture());
                            rowList.Add(square);
                        }
                        drawSquares.Add(rowList);
                    }

                    {
                        // 数字のマス追加
                        var square = new NumberSquare(rowNumberSquare.Count, -1);
                        asd.Engine.AddObject2D(square.getBackTexture());
                        asd.Engine.AddObject2D(square.getTextObject());
                        List<NumberSquare> list = new List<NumberSquare>();
                        list.Add(square);
                        rowNumberSquare.Add(list);
                    }
                }
            }
            while(col != drawSquares[0].Count)
            {
                if(drawSquares[0].Count > col)
                {
                    {
                        // マスの削除
                        foreach (var r in drawSquares)
                        {
                            asd.Engine.RemoveObject2D(r[r.Count - 1].getBackTexture());
                            r.RemoveAt(r.Count - 1);
                        }
                    }

                    {
                        // 数字のマスの削除
                        var colList = colNumberSquare[colNumberSquare.Count - 1];
                        foreach (var s in colList)
                        {
                            asd.Engine.RemoveObject2D(s.getBackTexture());
                            asd.Engine.RemoveObject2D(s.getTextObject());
                        }
                        colNumberSquare.RemoveAt(colNumberSquare.Count - 1);
                    }
                }
                else
                {
                    {
                        // マスの追加
                        int rowindex = 0;
                        int colindex = drawSquares[0].Count;
                        foreach (var r in drawSquares)
                        {
                            var square = new DrawSquare(rowindex, colindex);
                            asd.Engine.AddObject2D(square.getBackTexture());
                            r.Add(square);
                            rowindex++;
                        }
                    }

                    {
                        // 数字のマス追加
                        var square = new NumberSquare(-1, colNumberSquare.Count);
                        asd.Engine.AddObject2D(square.getBackTexture());
                        asd.Engine.AddObject2D(square.getTextObject());
                        List<NumberSquare> list = new List<NumberSquare>();
                        list.Add(square);
                        colNumberSquare.Add(list);
                    }
                }
            }
        });

動作はこんな感じになりました。

次回は実際に数字を入力する処理を追加します。

【C#】【ピクロス】【ALTSEED】実際にサイズを変更する

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

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

https://github.com/takishita2nd/Picross

前回はサイズ変更ダイアログに数字を入力するところまでできましたので、今回は実際にその数字を反映させる処理を作成していきます。

まずは、ダイアログからROWとCOLを取得する処理を追加します。

class Dialog
{
        public int GetRowValue()
        {
            return int.Parse(_rowValue);
        }

        public int GetColValue()
        {
            return int.Parse(_colValue);
        }

これをダイアログを表示する側で使用します。

        dialog.SetAction(() =>
        {
            dialog.Hide();
            int row = dialog.GetRowValue();
            int col = dialog.GetColValue();
            while(row != drawSquares.Count)
            {
                if(drawSquares.Count > row)
                {
                    var rowList = drawSquares[drawSquares.Count - 1];
                    foreach(var c in rowList)
                    {
                        asd.Engine.RemoveObject2D(c.getBackTexture());
                    }
                    drawSquares.RemoveAt(drawSquares.Count - 1);
                }
                else
                {
                    List<DrawSquare> rowList = new List<DrawSquare>();
                    for (int c = 0; c < drawSquares[0].Count; c++)
                    {
                        var square = new DrawSquare(drawSquares.Count, c);
                        asd.Engine.AddObject2D(square.getBackTexture());
                        rowList.Add(square);
                    }
                    drawSquares.Add(rowList);
                }
            }
            while(col != drawSquares[0].Count)
            {
                if(drawSquares[0].Count > col)
                {
                    foreach(var r in drawSquares)
                    {
                        asd.Engine.RemoveObject2D(r[r.Count - 1].getBackTexture());
                        r.RemoveAt(r.Count - 1);
                    }
                }
                else
                {
                    int rowindex = 0;
                    int colindex = drawSquares[0].Count;
                    foreach (var r in drawSquares)
                    {
                        var square = new DrawSquare(rowindex, colindex);
                        asd.Engine.AddObject2D(square.getBackTexture());
                        r.Add(square);
                        rowindex++;
                    }
                }
            }
        });

SetAction()には、ダイアログでOKをクリックしたときに実行される処理をラムダ式で記述しています。

ダイアログを非表示にすると同時にROW、COLを取得し、その値に合わせて実際のピクロスのサイズを変更します。

ROWの値を変えると縦方向にマスが増えたり減ったり、COLの値を変えると横方向に、という感じです。

これはまずはROW、COLが増えたかどうかを判断し処理を変える必要があります。

ROWを変える場合は、マスのLISTオブジェクト

List<List<DrawSquare>> drawSquares

これの一番外のLISTを追加、削除します。

COLを変える場合は内側のLISTにオブジェクトを追加、削除します。

あと、実際に動かしてみて分かったのですが、エンジンにテクスチャなどのオブジェクトを追加する順番によって表示に影響が出る(後に追加した方が上に上書きされる)ので、

それを防ぐために、テクスチャの描画プライオリティを設定する必要がありました。

今回は

マスオブジェクト<<<<<ダイアログ<<パレット

の順番にプライオリティを設定しました。

動作結果はこうなりました。

ようやくここまで来たかって感じです。

次回はピクロス解析のカギとなる数字を入力する部分を作成していきます。

【C#】【ピクロス】【ALTSEED】パレットから数字を入力する

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

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

https://github.com/takishita2nd/Picross

パレットの数字をクリックすることでダイアログの数字を入力する処理を作っていきます。

主にDialogクラスの実装です。

class Dialog
{
    private string _rowValue = "10";
    private string _colValue = "10";
    private TextBox _selectedTextBox = null;
    private string _selectedValue = string.Empty;

        public void OnClick(asd.Vector2DF pos)
        {
            if (_palette.IsShow() == false)
            {
                if (_rowText.isClick(pos))
                {
                    _selectedTextBox = _rowText;
                    _selectedValue = _rowValue;
                    _palette.Show(pos);
                }
                if (_colText.isClick(pos))
                {
                    _selectedTextBox = _colText;
                    _selectedValue = _colValue;
                    _palette.Show(pos);
                }
                if (_button.isClick(pos))
                {
                    _button.OnClick();
                }
            }
            else
            {
                if (_palette.IsClick(pos))
                {
                    string v = _palette.GetClickValue(pos);
                    _selectedValue = updateTextValue(_selectedTextBox, _selectedValue, v);
                    if (_selectedTextBox.Equals(_rowText))
                    {
                        _rowValue = _selectedValue;
                    }
                    else
                    {
                        _colValue = _selectedValue;
                    }
                }
                else
                {
                    _palette.Hide();
                    _selectedTextBox = null;
                    _selectedValue = string.Empty;
                }
            }
        }

        private string updateTextValue(TextBox textBox, string value, string v)
        {
            string text = string.Empty;
            switch (v)
            {
                case Palette.CODE.BS:
                    if(value.Length != 0)
                    {
                        text = value.Remove(value.Length - 1);
                    }
                    break;
                case Palette.CODE.CLR:
                    text = string.Empty;
                    break;
                default:
                    if(value.Length < 2)
                    {
                        text = value + v;
                    }
                    else
                    {
                        text = value;
                    }
                    break;
            }
            textBox.SetText(text);
            return text;
        }
    }

テキストボックスがROWとCOLの二つがあるので、テキストボックスをクリックしたときに、どちらをクリックしたかを記憶しておきます。

パレットがクリックされた場合、何がクリックされたかをパレットから取得します。

その値を確認し、その数字をテキストボックスの値に文字列追加していきます。

しかし、今のところ、数字は2桁までとしておきます。

なので、桁が2桁オーバーしたり、0桁以下にならないようにガードかけておきます。

あ、0を入力できないや。

次回までに修正しておきまーす。

【C#】【ピクロス】【ALTSEED】数字入力パレットの作成

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

最新ソースはこちら。

https://github.com/takishita2nd/Picross

サイズ変更ダイアログのテキストボックスをクリックすると、数字を入力するパレットを表示させます。

パレット自体は数独のときのものを流用します。

デザインを少し変更しています。

数独では「×」を表示していた部分を「←」(1文字消す)、「C」(全部消す)に置き換えます。

class Palette
{
    public class CODE
    {
        public const string BS = "BS";
        public const string CLR = "CLR";
    }

    private asd.Vector2DF palettePosition;
    private const int width = 192;
    private const int height = 256;
    private asd.TextureObject2D _texture;
    SquareObject[,] paletteSquareObjects = new SquareObject[3, 3];
    SquareObject paletteBSSquareObject = new SquareObject(-1, 0, "←");
    SquareObject paletteCRSquareObject = new SquareObject(-1, 1, "C");
    private bool _isShow = false;

    public Palette()
    {
        _texture = new asd.TextureObject2D();
        int value = 1;
        for (int row = 0; row < 3; row++)
        {
            for (int col = 0; col < 3; col++)
            {
                paletteSquareObjects[row, col] = new SquareObject(row, col, value.ToString());
                value++;
            }
        }
        paletteBSSquareObject.SetFontOffset(14, 9);
        paletteCRSquareObject.SetFontOffset(10, 9);
    }

    public void SetEngine()
    {
        asd.Engine.AddObject2D(_texture);
        for (int row = 0; row < 3; row++)
        {
            for (int col = 0; col < 3; col++)
            {
                asd.Engine.AddObject2D(paletteSquareObjects[row, col].getBackTexture());
                asd.Engine.AddObject2D(paletteSquareObjects[row, col].getTextObject());
            }
        }
        asd.Engine.AddObject2D(paletteBSSquareObject.getBackTexture());
        asd.Engine.AddObject2D(paletteBSSquareObject.getTextObject());
        asd.Engine.AddObject2D(paletteCRSquareObject.getBackTexture());
        asd.Engine.AddObject2D(paletteCRSquareObject.getTextObject());
    }

    public void Show(asd.Vector2DF pos)
    {
        palettePosition = new asd.Vector2DF(pos.X, pos.Y - 64);
        _texture.Position = palettePosition;
        _texture.Texture = Resource.getPaletteTexture();
        for (int row = 0; row < 3; row++)
        {
            for (int col = 0; col < 3; col++)
            {
                paletteSquareObjects[row, col].SetPosition(pos);
                paletteSquareObjects[row, col].Show();
            }
        }
        paletteBSSquareObject.SetPosition(pos);
        paletteBSSquareObject.Show();
        paletteCRSquareObject.SetPosition(pos);
        paletteCRSquareObject.Show();
        _isShow = true;
    }

    public void Hide()
    {
        for (int row = 0; row < 3; row++)
        {
            for (int col = 0; col < 3; col++)
            {
                paletteSquareObjects[row, col].Hide();
            }
        }
        paletteBSSquareObject.Hide();
        paletteCRSquareObject.Hide();
        _texture.Texture = null;
        _isShow = false;
    }

    public bool IsShow()
    {
        return _isShow;
    }

    public bool IsClick(asd.Vector2DF pos)
    {
        if (pos.X > palettePosition.X && pos.X < palettePosition.X + width
            && pos.Y > palettePosition.Y && pos.Y < palettePosition.Y + height)
        {
            return true;
        }
        else
        {
            return false;
        }
    }

    public void UpdateTexture(asd.Vector2DF pos)
    {
        for (int row = 0; row < 3; row++)
        {
            for (int col = 0; col < 3; col++)
            {
                paletteSquareObjects[row, col].UpdateTexture(pos);
            }
        }
        paletteBSSquareObject.UpdateTexture(pos);
        paletteCRSquareObject.UpdateTexture(pos);
    }

    public string GetClickValue(asd.Vector2DF pos)
    {
        for (int row = 0; row < 3; row++)
        {
            for (int col = 0; col < 3; col++)
            {
                if (paletteSquareObjects[row, col].isClick(pos) == true)
                {
                    return paletteSquareObjects[row, col].GetValue();
                }
            }
        }
        if (paletteBSSquareObject.isClick(pos))
        {
            return CODE.BS;
        }
        if (paletteBSSquareObject.isClick(pos))
        {
            return CODE.CLR;
        }

        return string.Empty;
    }
}
class SquareObject : ObjectBase
{
    public enum FontColor
    {
        Black,
        Red
    }

    protected int _row;
    protected int _col;
    private string _value;
    protected const int offsetX = 10;
    protected const int offsetY = 10;
    protected int fontOffsetX = 19;
    protected int fontOffsetY = 9;

    public SquareObject(int row, int col, string val)
    {
        width = 64;
        height = 64;
        _row = row;
        _col = col;
        _value = val;
        _x = col * width + offsetX;
        _y = row * height + offsetY;

        _backTexture = new asd.TextureObject2D();
        _backTexture.Position = new asd.Vector2DF(_x, _y);

        _valueText = new asd.TextObject2D();
        _valueText.Font = Resource.getPaletteFont();
        _valueText.Position = new asd.Vector2DF(_x + fontOffsetX, _y + fontOffsetY);

    }

    public void SetPosition(asd.Vector2DF pos)
    {
        _x = _col * width + (int)pos.X;
        _y = _row * height + (int)pos.Y;

        _backTexture.Position = new asd.Vector2DF(_x, _y);
        _valueText.Position = new asd.Vector2DF(_x + fontOffsetX, _y + fontOffsetY);
    }

    public void SetFontOffset(int x, int y)
    {
        fontOffsetX = x;
        fontOffsetY = y;
    }

    public void Show()
    {
        _valueText.Text = _value;
    }

    public void Hide()
    {
        _backTexture.Texture = null;
        _valueText.Text = "";
    }

    public void UpdateTexture(asd.Vector2DF pos)
    {
        if (pos.X > _x && pos.X < _x + width
            && pos.Y > _y && pos.Y < _y + height)
        {
            _backTexture.Texture = Resource.getPaletteSquareTexture();
        }
        else
        {
            _backTexture.Texture = null;
        }
    }

    public string GetValue()
    {
        return _value;
    }

    public void SetFontColor(FontColor color)
    {
        switch (color)
        {
            case FontColor.Black:
                _valueText.Font = Resource.getPaletteFont();
                break;
            case FontColor.Red:
                _valueText.Font = Resource.getFontRed();
                break;
            default:
                break;
        }
    }
}

あと、数字を表示するマスと数字以外を表示するマスとでクラスを分けていましたが、今回は同じクラスでやります。

class Dialog
{
    private Palette _palette = null;

    public Dialog()
    {
        _isShow = false;
        _palette = new Palette();
        _palette.Hide();

        // 確定ボタン
        _button = new Button(436, 400, "確定");
        _button.SetFontOffset(46, 4);
        _button.SetAction(() =>
        {
            if(_action != null)
            {
                _action.Invoke();
            }
        });
        // ラベル
        _label1 = new Label(320, 330, "↓");
        _label2 = new Label(490, 330, "→");
        // Row入力エリア
        _rowText = new TextBox(350, 330, "0");
        // Col入力エリア
        _colText = new TextBox(520, 330, "0");

        _texture = new asd.TextureObject2D();
        _texture.Position = new asd.Vector2DF(_x, _y);
    }

    public void SetEngine()
    {
        asd.Engine.AddObject2D(_texture);
        asd.Engine.AddObject2D(_label1.getTextObject());
        asd.Engine.AddObject2D(_label2.getTextObject());
        asd.Engine.AddObject2D(_rowText.getBackTexture());
        asd.Engine.AddObject2D(_rowText.getTextObject());
        asd.Engine.AddObject2D(_colText.getBackTexture());
        asd.Engine.AddObject2D(_colText.getTextObject());
        asd.Engine.AddObject2D(_button.getBackTexture());
        asd.Engine.AddObject2D(_button.getTextObject());
        _palette.SetEngine();
    }

    public void UpdateTexture(asd.Vector2DF pos)
    {
        if (_palette.IsShow())
        {
            _palette.UpdateTexture(pos);
        }
        else
        {
            _rowText.UpdateTexture(pos);
            _colText.UpdateTexture(pos);
            _button.UpdateTexture(pos);
        }
    }

    public void OnClick(asd.Vector2DF pos)
    {
        if (_palette.IsShow() == false)
        {
            if (_rowText.isClick(pos))
            {
                _palette.Show(pos);
            }
            if (_colText.isClick(pos))
            {
                _palette.Show(pos);
            }
            if (_button.isClick(pos))
            {
                _button.OnClick();
            }
        }
        else
        {
            if (_palette.IsClick(pos))
            {

            }
            else
            {
                _palette.Hide();
            }
        }
    }
}

動作はこんなかんじになりました。

【C#】【ピクロス】【ALTSEED】サイズ変更ダイアログのUIを作成

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

最新ソースはこちら。

https://github.com/takishita2nd/Picross

サイズ変更ダイアログにラベルとテキストボックスを追加します。

これらは新規クラスですね。

なので、ラベルクラスとテキストボックスクラスを作成します。

class Label : ObjectBase
{
    private string _text;
    private int fontOffsetX = 9;
    private int fontOffsetY = 4;
    private bool _isShow = false;

    public Label(int x, int y, string text)
    {
        width = 128;
        height = 32;
        _x = x;
        _y = y;
        _text = text;

        _valueText = new asd.TextObject2D();
        _valueText.Text = _text;
        _valueText.Font = null;
        _valueText.Position = new asd.Vector2DF(_x + fontOffsetX, _y + fontOffsetY);
    }

    public void SetFontOffset(int x, int y)
    {
        fontOffsetX = x;
        fontOffsetY = y;
        _valueText.Position = new asd.Vector2DF(_x + fontOffsetX, _y + fontOffsetY);
    }

    public void Show()
    {
        _isShow = true;
        _valueText.Font = Resource.getFont();
    }

    public void Hide()
    {
        _isShow = false; ;
        _valueText.Font = null;
    }
}
class TextBox : ObjectBase
{
    private string _text;
    private int fontOffsetX = 9;
    private int fontOffsetY = 4;
    protected bool enable = true;
    private Action _action = null;
    private bool _isShow = false;

    public TextBox(int x, int y, string text)
    {
        width = 128;
        height = 32;
        _x = x;
        _y = y;
        _text = text;

        _backTexture = new asd.TextureObject2D();
        _backTexture.Position = new asd.Vector2DF(_x, _y);

        _valueText = new asd.TextObject2D();
        _valueText.Text = _text;
        _valueText.Font = null;
        _valueText.Position = new asd.Vector2DF(_x + fontOffsetX, _y + fontOffsetY);
    }

    public void SetFontOffset(int x, int y)
    {
        fontOffsetX = x;
        fontOffsetY = y;
        _valueText.Position = new asd.Vector2DF(_x + fontOffsetX, _y + fontOffsetY);
    }

    public void UpdateTexture(asd.Vector2DF pos)
    {
        if (pos.X > _x && pos.X < _x + width
            && pos.Y > _y && pos.Y < _y + height)
        {
            _backTexture.Texture = Resource.getSelectedTextTexture();
        }
        else
        {
            _backTexture.Texture = Resource.getTextTexture();
        }
    }

    public void Show()
    {
        _isShow = true;
        _backTexture.Texture = Resource.getTextTexture();
        _valueText.Font = Resource.getFont();
    }

    public void Hide()
    {
        _isShow = false; ;
        _backTexture.Texture = null;
        _valueText.Font = null;
    }

    public void SetAction(Action action)
    {
        _action = action;
    }

    public void SetEnable(bool enable)
    {
        this.enable = enable;
    }

    public virtual void OnClick()
    {
        if (enable)
        {
            if (_action != null)
            {
                _action.Invoke();
            }
        }
    }
}

OnClick処理は後々修正するかもしれない。

そして、ほとんどはButtonクラスのアレンジ。

これをDialogクラスで使用します。

class Dialog
{
    private asd.TextureObject2D _texture;
    private bool _isShow;
    private Button _button = null;
    private TextBox _rowText = null;
    private TextBox _colText = null;
    private Label _label1 = null;
    private Label _label2 = null;
    private int _x = 300;
    private int _y = 300;
    private int _width = 400;
    private int _height = 200;
    private Action _action = null;

    public Dialog()
    {
        _isShow = false;

        // 確定ボタン
        _button = new Button(436, 400, "確定");
        _button.SetFontOffset(46, 4);
        _button.SetAction(() =>
        {
            if(_action != null)
            {
                _action.Invoke();
            }
        });
        // ラベル
        _label1 = new Label(320, 330, "↓");
        _label2 = new Label(490, 330, "→");
        // Row入力エリア
        _rowText = new TextBox(350, 330, "0");
        // Col入力エリア
        _colText = new TextBox(520, 330, "0");

        _texture = new asd.TextureObject2D();
        _texture.Position = new asd.Vector2DF(_x, _y);
    }

    public void SetEngine()
    {
        asd.Engine.AddObject2D(_texture);
        asd.Engine.AddObject2D(_label1.getTextObject());
        asd.Engine.AddObject2D(_label2.getTextObject());
        asd.Engine.AddObject2D(_rowText.getBackTexture());
        asd.Engine.AddObject2D(_rowText.getTextObject());
        asd.Engine.AddObject2D(_colText.getBackTexture());
        asd.Engine.AddObject2D(_colText.getTextObject());
        asd.Engine.AddObject2D(_button.getBackTexture());
        asd.Engine.AddObject2D(_button.getTextObject());
    }

    public void SetAction(Action action)
    {
        _action = action;
    }

    public void Show()
    {
        _isShow = true;
        _texture.Texture = Resource.getDialogTexture();
        _label1.Show();
        _label2.Show();
        _rowText.Show();
        _colText.Show();
        _button.Show();
    }

    public void Hide()
    {
        _isShow = false;
        _texture.Texture = null;
        _label1.Hide();
        _label2.Hide();
        _rowText.Hide();
        _colText.Hide();
        _button.Hide();
    }

    public bool IsShow()
    {
        return _isShow;
    }

    public void UpdateTexture(asd.Vector2DF pos)
    {
        _rowText.UpdateTexture(pos);
        _colText.UpdateTexture(pos);
        _button.UpdateTexture(pos);
    }

UI自分で作るのってめんどくさいね。

こんなのもう二度とやらない。

【C#】【ピクロス】【ALTSEED】サイズ変更ダイアログを大幅に修正

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

最新ソースはこちら。

https://github.com/takishita2nd/Picross

ダイアログ処理を大幅に修正しました。

具体的には、ボタン押下処理をラムダ式で与える、という形にしました。

    class Button : ObjectBase
    {
        private Action _action = null;

        public void SetAction(Action action)
        {
            _action = action;
        }

        public virtual void OnClick()
        {
            if(_action != null)
            {
                _action.Invoke();
            }
        }
        sizeButton.SetAction(() => {
            dialog.Show();
        });

ダイアログも同じように、

    class Dialog
    {
        private Action _action = null;

        public Dialog()
        {
            _isShow = false;
            _button = new Button(436, 450, "確定");
            _button.SetFontOffset(46, 4);
            _button.SetAction(() =>
            {
                if(_action != null)
                {
                    _action.Invoke();
                }
            });

            _texture = new asd.TextureObject2D();
            _texture.Position = new asd.Vector2DF(_x, _y);
        }

        public void SetAction(Action action)
        {
            _action = action;
        }

        public void OnClick(asd.Vector2DF pos)
        {
            if (_button.isClick(pos))
            {
                _button.OnClick();
            }
        }
            Dialog dialog = new Dialog();
            dialog.SetEngine();
            dialog.SetAction(() =>
            {
                dialog.Hide();
            });

特にUI周りは、オブジェクトが持つデータの引き渡しをどうするか、で頭を悩ませてしまいます。

こういうときは、慣れないと見た目複雑に見えてしまいますが、慣れればラムダ式にしてしまった方が、返ってコードがスッキリするんです。

実際、こうすることで、クラスの数を削減できました。

ボタン押下時の処理もラムダ式にすることで、実際の処理は作成元で定義すれば良いので、Buttonクラス一個で済んでしまいます。

UI周り自作するって予想以上に難しいね。

この調子でどんどん作ってきましょう。