【マインクラフト】Realmと自前サーバ、どっちが良いの?

みんなそんなにマイクラサーバを立てたいのか。

マインクラフトのサーバを自前で利用することのメリット。

それはハードウェアがあれば無料で運用することができる。

しかし、ハードルはいくつかありまして、

まず、サーバをどこに設置するか。

自宅にサーバを設置する場合、プレイヤーは同じLANの中からアクセスしなければならない。

自宅サーバを外部に開放する場合は、それなりの専門知識が必要だし、セキュリティリスクも発生します。

そこでVPSにサーバを設置するという方法もあります。

VPSを提供するサービスの中には、マインクラフト用テンプレートがあらかじめ用意されているものもあり、それを利用すれば、簡単にマインクラフトサーバを立ち上げることができます。

では、料金的にどうなのか?

Realmを使用する場合、統合版では10プレイヤー用が月470円、10プレイヤー用が月930円です。

Java版の場合は10プレイヤー用が月904円で提供されています。

VPSを使用する場合、Conohaだと月880円~で利用できます。

https://www.conoha.jp/vps/function/minecraft/

プレイヤーの制限はありませんが、利用プレイヤーが増えれば、それだけ要求されるスペックが高くなり、その分コストは高くなります。

これだけ見ると、VPSの方が遙かにコストが高いっすね。

サーバが重くなると、地形の描画に時間がかかったり、ゲームが少し前の状態に巻き戻ってしまう、などの動作異常が頻発します。

特にエリトラで飛行する場合は注意。

たぶん推奨スペックでもエリトラで地図作成する場合はかなりサーバに負荷がかかります。

と言うことで、まとめ。

自宅ネットワークでプレイ→自前サーバ

遠隔地から少人数でプレイ(10人まで)→Realm

大人数でプレイ(11人以上)→VPS

マニアックで物好きな人→VPS

サーバの運用が分からない人→Realm

一人でプレイする人→ローカルで良いんじゃない?

【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();
                    }
                }
            }
        });

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

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

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

新型コロナウイルス感染症緊急経済対策の特別定額給付金申請(札幌市の場合)

札幌市のHPが更新され、5月18日から申請の送付を行うとのことです。

https://www.city.sapporo.jp/2019n-cov/hokenfukushi/teigakukyuhu.html

オンライン申請は5月18日頃に受け付けが開始出来る見込み、とのことです。

その前に。

上にいろいろと注意事項が書いてありますが、

オンライン申請するにはいろいろと条件があるようで、

ざっくり言うと、

e-Taxできる状態ならオンラインで受け付けるよ

ということです。

上で書いてる、電子証明書とは、e-Taxで使用するもので、マイナンバーカード発行と同時に申請した(様な気がする)ものです。

これが有効期限内である必要があります。

有効期限が切れていた場合は、区役所に行って更新する必要があります。

手続き自体は窓口で暗証番号を入力して当日完了しますが、混雑が予想されるため、大人しく郵送にした方が良いと思います。

あ、有効期限が切れた場合は、更新のお手紙が届くと思うよ。

暗証番号忘れた人は論外。

PCで申請する場合はカードリーダーが必要になります。

カードリーダーが無い場合は、NFCに対応したスマートフォン(iPhone等)があればスマホで申請できます。

最近のスマホだったら大抵対応しているよ。

ここまで読んで、理解できない人は、郵送で手続きしてください。

【バンドリ】【ガルパ】真の効率曲

たぶん、多くのバンドリーマーは、ガルパで「効率曲」というと、「天下トーイツAtoZ」が連想されると思う。

いや、間違ってはいないのだが。

答えはこちらのリンク先にありまして、

https://appmedia.jp/bang_dream/730842

このページの真ん中当たりに、効率曲ランキングみたいなものがリストで掲載されています。

このページ、ブックマークに入れておくと便利よ。

新曲が追加されると、随時更新されるので。

効率曲というのは、まぁ、一言で言うと、1回のライブで高得点が狙える曲のこと。

1回のライブで高得点が出る、ということは、周回すれば、イベントポイントをたくさん稼げる、と言うことです。

しかも、高得点が出るということは、ハイスコアレーティングの更新にも役に立ちます。

ただ、ライブ中に死亡してしまうのは論外として、ミスが多くなってしまうと効率曲を周回する意味が無くなってしまいます。

協力ライブで、自分がクリアできても、他プレイヤーが死んでしまっては意味が無いので。

なので、比較的簡単な、楽曲レベルが低い曲の方が良い。

効率曲のリストを見てみると、上位の方に高難易度の曲が集中していると思います。

なので、オイラは、これらを考慮した上で、簡単かつハイスコアが狙える真の効率曲の調べてみました。

実際にプレイしてハイスコアレーティング算出しています。

Poppin’Party

  • Jumpin'(レベル26)
  • B.O.F(レベル25)
  • Home Street(レベル25)

Afterglow

  • アスノヨゾラ哨戒班(レベル26)
  • 瞬間センチメンタル(レベル26)
  • 午後の待ち合わせ(レベル26)

PastelPalettes

  • 天下トーイツAtoZ☆(SP レベル25)
  • ふわふわ時間(レベル25)
  • 春擬き(レベル25)

Roselia

  • Hacking to the Gate(レベル25)
  • シャルル(SP レベル25)
  • 残酷な天使のテーゼ(レベル25)

ハロー、ハッピーワールド!

  • 太陽曰く燃えよカオス(レベル26)
  • コレカラ(レベル25)
  • ゴーカ!ごーかい!?ファントムシーフ!(SP レベル25)

Morfonica

  • chAngE(レベル26)
  • ブルームブルーム(レベル26)
  • 秒針を噛む(レベル26)

RAISE A SUILEN

  • ヒトリノ夜(レベル26)
  • EXPOSE ‘Burn out!!!'(レベル26)
  • UNSTOPPABLE(レベル26)

その他

  • す、好きなんかじゃない!(レベル25)
  • 大盛り一丁!ガルパ☆ピコ(レベル26)
  • クインティブル☆すまいる(レベル25)

このあたりなら、ハイレベルプレイヤーでなくてもハイスコアが狙えますし、効率よく周回できると思います。

ただし、協力ライブで同じ曲ばかり選んでいると、他プレイヤーを苛つかせて切断する可能性があります。

みんなAtoZばかり選んで「またか・・・」って思ったことありますよね??

なので、そこら辺はうまくバランスを取って。

【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);
                    }
                }

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

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

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

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

【ラズパイ】Sense HATのジャイロセンサー

Sense Hatのジャイロセンサーを使っていろいろやってみようと思ったものの、思ったような結果が得られなかったので、もっとドキュメントを読み込んでいこうと思います。

ドキュメントはこれ(英語)。

https://github.com/raspberrypilearning/astro-pi-guide/blob/master/sensors/movement.md

Sense HatにはIMUというものが搭載されているらしい。

IMUにはジャイロセンサー、加速度センサー、方位磁石が搭載されています。

向きは、Pitch、Roll、Yawの三つの軸で表されます。

まぁ、これはこないだ見た。

この動画で三つの軸の関係性が分かります。

これに関するサンプルプログラムがありました。

使い方書いてあるけど、このやり方では動かなかったので、実際に試したやり方を書いておきます。

> git clone git://github.com/astro-pi/apollo-soyuz
> cd apollo-soyuz
> sudo pip3 install pi3d
> sudo ./soyuz.py

SDカードスロットがメインブースターになるイメージです。

実際に動かしてみました。

Roll軸で回転させてみたところ、それに伴って向き自体が左右に動いている事が分かりました。

Sense Hat(IMU)に搭載されているジャイロセンサーは宇宙船を操作することに特化されていて、単純に傾けたときの3軸の角度が得られる訳では無い(ような気がする)。

宇宙船補正みたいなものがあるんじゃないか。

しらんけど。

多分、思ったような数値が出なかったのは、このせいなんだろうな。

そうだよな、Sense Hatって宇宙の科学実験用のアタッチメントだもんな。

まぁ、ちょっと宇宙船の技術に触れられたのは良かったと思う(という風に、前向きに捉える)。

【ラズパイ】受信したセンサーデータを表示する。

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

前回はラズパイからセンサーで収集したデータを送信させていましたが、フォーマットがJsonなので、このままでは使用できません。

ラズパイ側のソースコードをgitHubにアップしました。

https://github.com/takishita2nd/pi/blob/master/http.py

今回はこのデータを見やすいように加工します。

GUIのほうが見やすいと思って、軽い気持ちで作ってみたのですが、思った以上にがっつりなことになってしまいました。

WPFを使用しようと思うのですが、今回はPrismという拡張機能を使用してMVVMモデルで作成しようと思います。

メニューの拡張機能からPrismを検索し、Prismをインストールします。

Visual Studioを再起動すると、新規プロジェクトにPrismが追加されているので、Prismのプロジェクトを作成します。

まずは、XAMLの作成。

<Window x:Class="BlankApp1.Views.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:prism="http://prismlibrary.com/"
        prism:ViewModelLocator.AutoWireViewModel="True"
        Title="{Binding Title}" Height="180" Width="350">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition/>
            <RowDefinition/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <Label Grid.Row="0" Grid.Column="0" Content="温度" HorizontalAlignment="Center" VerticalAlignment="Center"/>
        <Label Grid.Row="1" Grid.Column="0" Content="湿度" HorizontalAlignment="Center" VerticalAlignment="Center"/>
        <Label Grid.Row="2" Grid.Column="0" Content="気圧" HorizontalAlignment="Center" VerticalAlignment="Center"/>
        <Label Grid.Row="0" Grid.Column="1" Content="{Binding Temperature}"  HorizontalAlignment="Center" VerticalAlignment="Center"/>
        <Label Grid.Row="1" Grid.Column="1" Content="{Binding Humidity}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
        <Label Grid.Row="2" Grid.Column="1" Content="{Binding Pressure}"  HorizontalAlignment="Center" VerticalAlignment="Center"/>
        <Button Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="2" Content="Monitor" Command="{Binding ButtonClickCommand}" HorizontalAlignment="Center" VerticalAlignment="Center" Height="20" />
    </Grid>
</Window>

温度、湿度、気圧を表示するだけです。

あとはデータの待ち受けを開始するボタンを配置します。

Jsonを展開するためのクラスを定義します。

    [JsonObject("sensorModel")]
    class Sensor
    {
        [JsonProperty("temperature")]
        public int Temperature { get; set; }
        [JsonProperty("humidity")]
        public int Humidity { get; set; }
        [JsonProperty("pressure")]
        public int Pressure { get; set; }
    }

クラス名は何でもいいですが、パラメータの名前は送信側と合わせる必要があります。

最後にViewModelの処理です。

    public class MainWindowViewModel : BindableBase
    {
        private string _title = "ラズパイモニター";
        public string Title
        {
            get { return _title; }
            set { SetProperty(ref _title, value); }
        }
        private int _temperature;
        private int _humidity;
        private int _pressure;
        public int Temperature 
        { 
            get { return _temperature; } 
            set { SetProperty(ref _temperature, value); }
        }
        public int Humidity
        {
            get { return _humidity; }
            set { SetProperty(ref _humidity, value); }
        }
        public int Pressure
        {
            get { return _pressure; }
            set { SetProperty(ref _pressure, value); }
        }

        public DelegateCommand ButtonClickCommand { get; }

        public MainWindowViewModel()
        {
            ButtonClickCommand = new DelegateCommand(() =>
            {
                Thread thread = new Thread(new ThreadStart(() => {
                    try
                    {
                        HttpListener listener = new HttpListener();
                        listener.Prefixes.Add("http://192.168.1.3:8000/");
                        listener.Start();
                        while (true)
                        {
                            HttpListenerContext context = listener.GetContext();
                            HttpListenerRequest req = context.Request;
                            using (StreamReader reader = new StreamReader(req.InputStream, req.ContentEncoding))
                            {
                                string s = reader.ReadToEnd();
                                Sensor sensor = JsonConvert.DeserializeObject<Sensor>(s);
                                Temperature = sensor.Temperature;
                                Humidity = sensor.Humidity;
                                Pressure = sensor.Pressure;
                            }
                            HttpListenerResponse res = context.Response;
                            res.StatusCode = 200;
                            byte[] content = Encoding.UTF8.GetBytes("HELLO");
                            res.OutputStream.Write(content, 0, content.Length);
                            res.Close();
                        }
                    }
                    catch (Exception ex)
                    {
                    }
                }));

                thread.Start();

            });
        }
    }

ボタンを押すとHTTPを待ち受け開始し、HTTPを受信したらデータ(Json)を展開しオブジェクト化します。

そして、各パラメータをプロパティに設定します。

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

うん、想定通り。

【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);
                    }
                }
            }
        });

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

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

【ラズパイ】ネットワークでセンサーデータを送信する。

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

今回は実際にセンサーの情報をWindows側に送信したいと思います。

ラズパイ側のPythonソースをこのように作成しました。

import json
import urllib.request
import time
from sense_hat import SenseHat

sense = SenseHat()

url = 'http://192.168.1.3:8000'
headers = {
    'Content-Type': 'application/json',
}

while True:
    temp = int(sense.get_temperature())
    hum = int(sense.get_humidity())
    press = int(sense.get_pressure())
    data = {
        'temperature': temp,
        'humidity': hum,
        'pressure': press,
    }
    req = urllib.request.Request(url, json.dumps(data).encode(), headers)
    with urllib.request.urlopen(req) as res:
        body = res.read()
    time.sleep(1)

センサーから温度、湿度、気圧の測定データを取得し、このデータをJson形式にして送信しています。

この処理を1秒周期で実行します。

最近のWebAPIは送受信するデータの量が増えてきているので、どのようなデータ形式も一行の文字列で表現できるJsonを使用するのが今の一般的です。

Windows側(サーバ側)の出力はこうなりました。(受信側のコードは前回から変更していません。)

温度と湿度は、ラズパイ本体からの熱の影響を受けているので、実際の部屋の中の数値とは異なりますが、まぁ、良い感じです。

自分、ぼっちですが何か?