「技術」カテゴリーアーカイブ

【C#】【ピクロス】【ALTSEED】解析パターン13

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

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

https://github.com/takishita2nd/Picross

次はここを処理します。

ここは数字が[3,1]とありますが、すでに3マス塗られているので、その上にある空白はマスクされるのが確定されています。

考え方は、

・数字の先頭が解析済みである。

・マスの中で塗られている該当箇所を特定する。

・その前の空白マスをマスクする。

このロジックで行けるはずです。

        // 解析パターンその13
        // 確実に塗れない空白部分をマスクする
        private void pattern13()
        {
            // Row
            pattern13RowFront();
            pattern13RowBack();
            // Col
            pattern13ColFront();
            pattern13ColBack();
        }

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

                // 先頭の数字が解析済みであるか?
                if (rowlist.AnalyzeDatas[0].IsAnalyzed() == false)
                {
                    row++;
                    rowlist.AnalyzeDatas.Reverse();
                    continue;
                }

                // マスクされていない連続したマスを数える
                var dataList = getSquareDataListUnMaskedRow(row);
                if(dataList == null)
                {
                    row++;
                    rowlist.AnalyzeDatas.Reverse();
                    continue;
                }

                // 解析済みのマスを確認する
                foreach(var data in dataList)
                {
                    bool check = false;
                    int count = 0;
                    foreach(var p in data)
                    {
                        if (p.IsPainted())
                        {
                            count++;
                        }
                        else
                        {
                            break;
                        }
                    }
                    if(count == rowlist.AnalyzeDatas[0].Value)
                    {
                        foreach(var data2 in dataList)
                        {
                            if (data2.Equals(data) == false)
                            {
                                foreach(var p in data2)
                                {
                                    p.Mask();
                                }
                            }
                            else
                            {
                                break;
                            }
                        }
                        check = true;
                    }
                    if (check)
                    {
                        break;
                    }
                }

                rowlist.AnalyzeDatas.Reverse();
                row++;
            }
        }

このパターンは前から確認するパターンと後ろから確認するパターン、それを行と列に対して行うようにしています。

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

だいぶ埋まってきましたね。

【LARAVEL】【ダイエット支援】ページネーションを実装する

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

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

https://github.com/takishita2nd/diet-mng

データ一覧画面にページネーション機能を追加します。

1画面に表示するデータの数を絞り、ページ切り替えによって表示するデータを切り替えるというものです。

データの数が多くなったらこういう機能も必要になると思います。

やり方は主に2つありまして、

一つは、一度すべてのデータを取得し、その後はフロント側でよしなにするパターン。

もう一つは、ページ切り替えをするごとに必要なデータを取得するパターンです。

今回は後者の方を採用しようと思います。

まずは、データ数からページ数を求める処理を作成し、ページ切り替えの機能を作成していきます。

リポジトリの実装。

    public function getTotalRecord($user)
    {
        return $user->WeightManagements()->count();
    }

count()でデータ数が取れるんですね。便利。

コントローラーの実装。

    /**
     * データのレコード数を取得する
     */
    public function total(Request $request)
    {
        return response()->json(['total' => $this->weightManagement->getTotalRecord(Auth::user())]);
    }
Route::post('api/weight/total', 'Weight\ApiController@total');

ページを表示したら、このAPIを使用するようにします。

    created: function() {
        this.updateList();
        this.createPagenate();
    },
    methods: {
        createPagenate: function() {
            var self = this;
            this.pagenates = [];
            axios.post('api/weight/total').then(function(response){
                var total = response.data.total;
                self.maxPage = Math.floor(total / 10) + 1;
                for(var i = 1; i <= self.maxPage; i++) {
                    self.pagenates.push(i);
                }
            }).catch(function(error){
            });
        },

APIでデータ数を取得し、そこからページ数とページネーションのリスト(1,2,3…)をリストで作成します。

これを使用して、テンプレートを作成します。

            <div id="pagenate">
                <ul>
                    <li>
                        <a href="#" v-if="prevShow" @click="prevPage()">&lt;</a>
                        <b v-else>&lt;</b>
                    </li>
                    <li v-for="page in pagenates">
                        <a href="#" v-if="currentPage != page" @click="changePage(page)">{{ page }}</a>
                        <b v-else>{{ page }}</b>
                    </li>
                    <li>
                        <a href="#" v-if="nextShow" @click="nextPage()">&gt;</a>
                        <b v-else>&gt;</b>
                    </li>
                </ul>
            </div>

前のページ(<)、ページ数指定、次のページ(>)の順に表示させています。

ただ、1ページ目に場合は前のページが非活性化、最大ページのときは次のページが非活性化、あとは、現在のページと同じページへのリンクが非活性化させる必要があります。

これはv-ifで表示を切り替えています。

判定はcomputedで実装しています。

    computed: {
        prevShow: function() {
            return this.currentPage != 1;
        },
        nextShow: function() {
            return this.currentPage != this.maxPage;
        },
    },

あとは、ページをクリックしたときの処理を作成していきます。

        changePage: function(page) {
            this.currentPage = page;
            this.updateList();
        },
        nextPage: function() {
            this.currentPage += 1;
            if(this.currentPage > this.maxPage) {
                this.currentPage = this.maxPage;
            }
            this.updateList();
        },
        prevPage: function() {
            this.currentPage -= 1;
            if(this.currentPage <= 0) {
                this.currentPage = 0;
            }
            this.updateList();
        },

リスト更新処理も修正します。

        updateList: function() {
            this.datalists = [];
            this.contents.page = this.currentPage;
            this.param.contents = this.contents;
            var self = this;
            axios.post('api/weight/list', this.param).then(function(response){
                response.data.dataLists.forEach(element => {
                    self.datalists.push({
                        id: element.id,
                        date: element.datetime,
                        weight: element.weight,
                        fat_rate: element.fat_rate,
                        bmi: element.bmi
                    })
                });
            }).catch(function(error){
            });
        }
    /**
     * データを取得する
     */
    public function list(Request $request)
    {
        return response()->json(['dataLists' => $this->weightManagement->list(Auth::user(), $request->contents["page"])]);
    }
    public function list($user, $page = 1)
    {
        return $user->WeightManagements()
                    ->orderBy('datetime', 'desc')
                    ->skip(10 * ($page - 1))
                    ->limit(10)
                    ->get();
    }

日付で降順に並べ替え、ページの開始から10件取得する、という処理に変更しています。

実行結果はこちら。

【北海道大戦】マップに市町村名を表示する

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

gitHubにソースファイルを公開しました。

https://github.com/takishita2nd/HokkaidoWar

前回はただマップを表示するだけでしたが、

今回はマウスカーソルを移動させると、そのカーソル位置の市町村名を表示させるところまでやります。

そのためには、前回取り込んだJsonデータを扱いやすいように内部データに取り込む必要があります。

市町村は複数のマップで構成されているので、まずは、マップクラスを作成します。

    class Map
    {
        private int _x;
        private int _y;
        private asd.GeometryObject2D _geometryObj;

        private readonly int width = 24;
        private readonly int height = 24;
        private readonly int offsetx = 50;
        private readonly int offsety = 50;

        public Map(int x, int y, asd.Color color)
        {
            _x = x;
            _y = y;
            _geometryObj = new asd.GeometryObject2D();
            _geometryObj.Color = color;
            asd.Engine.AddObject2D(_geometryObj);
            var rect = new asd.RectangleShape();
            rect.DrawingArea = new asd.RectF(width * _x + offsetx, height * _y + offsety, width, height);
            _geometryObj.Shape = rect;
        }

        public void SetColor(asd.Color color)
        {
            _geometryObj.Color = color;
        }

        public bool IsOnMouse(asd.Vector2DF pos)
        {
            if (pos.X > width * _x + offsetx && pos.X < width * (_x + 1) + offsetx
                && pos.Y > height * _y + offsety && pos.Y < height * (_y + 1) + offsety)
            {
                return true;
            }
            else
            {
                return false;
            }
        }
    }

これを使用する都市クラスを作成します。

    class City
    {
        private string _name = string.Empty;
        private List<Map> _maps = null;
        private asd.Color _color;

        public City(string name, Point[] points)
        {
            _name = name;
            _maps = new List<Map>();
            var r = Singleton.GetRandom();
            _color = new asd.Color((byte)r.Next(0, 255), (byte)r.Next(0, 255), (byte)r.Next(0, 255));

            foreach (var p in points)
            {
                Map m = new Map(p.x, p.y, _color);
                _maps.Add(m);
            }
        }

        public void OnMouse(asd.Vector2DF pos)
        {
            foreach(var m in _maps)
            {
                if(m.IsOnMouse(pos))
                {
                    var info = Singleton.GetInfomationWindow();
                    info.ShowText(pos, _name);
                }
            }
        }

        public bool IsOnMouse(asd.Vector2DF pos)
        {
            bool ret = false;
            foreach (var m in _maps)
            {
                if (m.IsOnMouse(pos))
                {
                    ret = true;
                }
            }
            return ret;
        }
    }

と、ここでひょっこり出てきた都市名を表示する窓クラスも作成します。

    class InfomationWindow
    {
        private asd.TextObject2D _valueText;

        public InfomationWindow()
        {
            _valueText = new asd.TextObject2D();
            _valueText.Font = Singleton.GetFont();
            asd.Engine.AddObject2D(_valueText);
        }

        public void ShowText(asd.Vector2DF pos, string text)
        {
            _valueText.Text = text;
            _valueText.Position = new asd.Vector2DF(pos.X, pos.Y);
        }
    }

これをどこでも取り出せるようにシングルトンにします。

    class Singleton
    {
        private static InfomationWindow _info = null;
        private static Random random = null;
        private static asd.Font _font = null;

        public static Random GetRandom()
        {
            if(random == null)
            {
                random = new Random();
            }
            return random;
        }

        public static asd.Font GetFont()
        {
            if(_font == null)
            {
                _font = asd.Engine.Graphics.CreateFont("FontText.aff");
            }
            return _font;
        }

        public static InfomationWindow GetInfomationWindow()
        {
            if (_info == null)
            {
                _info = new InfomationWindow();
            }
            return _info;
        }
    }

最終的にこれらを組み合わせると、

        public void Run()
        {
            asd.Engine.Initialize("北海道大戦", 1800, 1000, new asd.EngineOption());

            // 下地
            var background = new asd.GeometryObject2D();
            asd.Engine.AddObject2D(background);
            var bgRect = new asd.RectangleShape();
            bgRect.DrawingArea = new asd.RectF(0, 0, 1800, 1000);
            background.Shape = bgRect;

            cities = new List<City>();
            var r = new Random();
            foreach (var map in mapData.list)
            {
                City city = new City(map.name, map.point);
                cities.Add(city);
            }

            while (asd.Engine.DoEvents())
            {
                asd.Vector2DF pos = asd.Engine.Mouse.Position;
                if (isOnMaouseMap(pos))
                {
                    foreach (var city in cities)
                    {
                        city.OnMouse(pos);
                    }
                }
                else
                {
                    var info = Singleton.GetInfomationWindow();
                    info.ShowText(pos, string.Empty);
                }

                asd.Engine.Update();
            }
            asd.Engine.Terminate();
        }

        private bool isOnMaouseMap(asd.Vector2DF pos)
        {
            bool ret = false;
            foreach (var city in cities)
            {
                if (city.IsOnMouse(pos))
                {
                    ret = true;
                }
            }
            return ret;
        }

実行結果はこちら。

【C#】【ピクロス】【ALTSEED】解析パターン12

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

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

https://github.com/takishita2nd/Picross

次はここを処理します。

数字が2つなのに対して、マスは2箇所、マスクされたマスを挟んで塗られているので、2つの数字はこのマスに繋がります。

なので、赤いところで囲まれた部分は塗ることができないのが分かります。

なので、ここをマスクします。

考え方は、マスクされていない部分のマスを抽出、リスト化し、塗られているマスを含むリストの数と数字の数が一致していた場合、塗られていないリストのマスをマスクする、という方法で行きます。

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

                // マスクされていない連続したマスを数える
                var data = getSquareDataListUnMaskedRow(row);
                if(data == null)
                {
                    row++;
                    rowlist.AnalyzeDatas.Reverse();
                    continue;
                }

                // 塗られているマスがあるところを確認する
                int i = 0;
                bool[] flg = new bool[data.Count];
                for(i = 0; i < data.Count; i++)
                {
                    flg[i] = false;
                }
                i = 0;
                int count = 0;
                foreach(var dataList in data)
                {
                    foreach(var p in dataList)
                    {
                        if (p.IsPainted())
                        {
                            flg[i] = true;
                            count++;
                            break;
                        }
                    }
                    i++;
                }

                // 数字の数とflgの数を比較
                if(rowlist.AnalyzeDatas.Count == count)
                {
                    // flgが立っていないマスをマスクする
                    i = 0;
                    foreach(var dataList in data)
                    {
                        if(flg[i] == false)
                        {
                            foreach(var p in dataList)
                            {
                                p.Mask();
                            }
                        }
                        i++;
                    }
                }

                rowlist.AnalyzeDatas.Reverse();
                row++;
            }
        }
        private List<List<BitmapData>> getSquareDataListUnMaskedRow(int row)
        {
            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)
            {
                return null;
            }

            return data;
        }

同じ処理をColに対しても行います。

実行結果はこちら。

だいぶ良い感じに埋まってきました。

【LARAVEL】【ダイエット支援】VPSにデプロイする

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

最低限必要な機能は出来上がったので、本番サーバであるVPSで稼働させます。

gitからソースファイルをクローン

git clone https://github.com/takishita2nd/diet-mng.git

パーミッション変更

chmod -R 777 storage/
chmod 777 bootstrap/cache/

.envを作成し、データベースの設定と、URLの設定を記入。

cp .env.example .env
vi .env

データベースにログインし、データベースを作成。

mysql> create database diet_mng;

composerを使ってLaravelの環境を構築

sudo apt-get install composer
composer install

.envにkyeを生成

php artisan key:generate

npmでVue.jsを使用できるようにする

sudo apt-get install npm
sudo apt-get install libpng-dev
npm install
npm run prod

データベース構築(マイグレート)

php artisan migrate

nginxの設定

URLでブログとダイエット管理を分けようと思ったのですが、上手く設定できなかったので、ポート番号で分けます。

cd /etc/nginx/sites-enabled
sudo cp default laravel
sudo vi laravel
server {
        listen 8443 ssl default_server;
        listen [::]:8443 ssl default_server;
        ssl_certificate     /etc/letsencrypt/live/taki-lab.site/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/taki-lab.site/privkey.pem;

        root /var/www/html/diet-mng/public;

        index index.php index.html index.htm index.nginx-debian.html;

        server_name taki-lab.site;

        location / {
                try_files $uri $uri/ /index.php?$query_string;
        }

        location ~ \.php$ {
                fastcgi_pass   unix:/run/php/php7.2-fpm.sock;
                fastcgi_index  index.php;
                fastcgi_param  SCRIPT_FILENAME  $document_root/index.php;
                include        fastcgi_params;
        }
}

ポート番号は8443を使用しました。

ブログ用の設定では動きませんので、いろんなサイトを調べた結果、このような設定で動作できます。

rootはプロジェクトディレクトリ/publicを指定します。publicの下のindex.phpにアクセスするように指定します。

nginxの設定と読み込み

sudo systemctl reload nginx.service

何も表示されなければ書式は合っています。

エラーが出たら/var/log/nginx/error.logを確認してください。

ここまで上手くいけばトップページが表示されるはず。

トップページ書き換えるの忘れてた。

ログインしてデータ入力できることを確認する。

以下、詰まったところ

modelに以下を記入しないとデータベースクエリが動かなかった。

protected $table = 'weight_managements';

https://qiita.com/igz0/items/d14fdff610dccadb169e

テーブル名を明示的に指定しないといけないらしい。

ここらへん、ローカル環境にフィードバックさせます。

というわけで、

ダイエット管理サイト、以下からアクセスできますので、よかったら使ってみてください。

https://taki-lab.site:8443/

【北海道大戦】マップデータを描画する

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

gitHubにソースファイルを公開しました。

https://github.com/takishita2nd/HokkaidoWar

前回作成したJsonファイルを読み込んで、画面に出力させたいと思います。

まずはJsonデータをオブジェクトで持つためのクラスを作成します。

    [JsonObject("MapDataModel")]
    public class MapData
    {
        [JsonProperty("list")]
        public List[] list { get; set; }
    }

    [JsonObject("List")]
    public class List
    {
        [JsonProperty("name")]
        public string name { get; set; }
        [JsonProperty("point")]
        public Point[] point { get; set; }
    }

    [JsonObject("Point")]
    public class Point
    {
        [JsonProperty("x")]
        public int x { get; set; }
        [JsonProperty("y")]
        public int y { get; set; }
    }

これは、Jsonをクリップボードにある状態で、「編集」→「形式を選択して貼り付け」→「Json」を選択すると簡単にJsonに合わせてクラスを作成してくれます。

では、実際にJsonを読み込んでオブジェクト化する処理。

    class FileAccess
    {
        private const string _filename = "hokkaido.json";
        public static MapData Load()
        {
            string str = string.Empty;
            using (var stream = new StreamReader(_filename, true))
            {
                str = stream.ReadToEnd();
            }
            return JsonConvert.DeserializeObject<MapData>(str);
        }
    }

これをAltseedで画面に表示させます。

    class HokkaidoWar
    {
        MapData mapData = null;
        public HokkaidoWar()
        {
            mapData = FileAccess.Load();
        }

        public void Run()
        {
            asd.Engine.Initialize("北海道大戦", 1800, 1000, new asd.EngineOption());

            // 下地
            var background = new asd.GeometryObject2D();
            asd.Engine.AddObject2D(background);
            var bgRect = new asd.RectangleShape();
            bgRect.DrawingArea = new asd.RectF(0, 0, 1800, 1000);
            background.Shape = bgRect;

            var r = new Random();
            foreach (var map in mapData.list)
            {
                var color = new asd.Color((byte)r.Next(0, 255), (byte)r.Next(0, 255), (byte)r.Next(0, 255));
                foreach (var point in map.point)
                {
                    var geometryObj = new asd.GeometryObject2D();
                    geometryObj.Color = color;
                    asd.Engine.AddObject2D(geometryObj);
                    var rect = new asd.RectangleShape();
                    rect.DrawingArea = new asd.RectF(24 * point.x + 50, 24 * point.y + 50, 24, 24);
                    geometryObj.Shape = rect;
                }
            }

            while (asd.Engine.DoEvents())
            {
                asd.Engine.Update();
            }
            asd.Engine.Terminate();
        }
    }
        static void Main(string[] args)
        {
            HokkaidoWar hokkaidoWar = new HokkaidoWar();
            hokkaidoWar.Run();
        }

色はとりあえず乱数で振ることにしました。

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

【ラズパイ】【GLCD】画面に時刻を表示する

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

最新ソースをgitHubにアップしました。

https://github.com/takishita2nd/GLCD

ここまで来たら、時刻を表示するくらい簡単にできると思うんよ。

def __main():
    PinsInit(20, 7, 8, 9, 18, 19, 10, 11, 12, 13, 14, 15, 16, 17)
    GLCDInit()
    GLCDDisplayClear()
    GLCDBox(0, 0, 127, 62)
    GLCDLine(0, 15, 127, 15)

    try:
        while True:
            GLCDPuts(30, 38, datetime.datetime.now().strftime('%H:%M:%S'))
            time.sleep(0.1)
    except KeyboardInterrupt:
        GLCDDisplayClear()
        GPIO.cleanup()

うん、これでラズパイで時計が作れる。

【ラズパイ】【GLCD】画面に直線を引く

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

最新ソースをgitHubにアップしました。

https://github.com/takishita2nd/GLCD

今回は画面に直線を引きます。

始点と終点の座標を与えると、直線を引くようにします。

傾きがあった場合でも引けるようにします。

といっても、サンプルをPythonに書き換えただけですが。

def GLCDLine(Xp0, Yp0, Xp1, Yp1):
    #差分の大きい方を求める
    steep = (abs(Yp1 - Yp0) > abs(Xp1 - Xp0))
    #X,Yの入れ替え
    if steep == True:
        x = Xp0
        Xp0 = Yp0
        Yp0 = x
        x = Xp1
        Xp1 = Yp1
        Yp1 = x
    if Xp0 > Xp1:
        x = Xp0
        Xp0 = Xp1
        Xp1 = x
        x = Yp0
        Yp0 = Yp1
        Yp1 = x
    #傾き計算
    deltax = Xp1 - Xp0
    deltay = abs(Yp1 - Yp0)
    er = 0
    y = Yp0
    ystep = 0
    #傾きでステップの正負を切り替え
    if Yp0 < Yp1:
        ystep = 1
    else:
        ystep = -1
    #直線を点で描画
    for x in range(Xp0, Xp1 + 1):
        if steep == True:
            GLCDPutPixel(y, x)
        else:
            GLCDPutPixel(x, y)
        er += deltay
        if (er << 1) >= deltax:
            y += ystep
            er -= deltax

まずどちらの方向に描画していくか(縦or横)を判定します。

基本的に絶対値が大きい方向に描画していきます。

縦方向に描画していく場合はx/yを入れ替えます。

あとは傾きに応じてy/xを加算しながらx/y方向に描画していきます。

def __main():
    PinsInit(20, 7, 8, 9, 18, 19, 10, 11, 12, 13, 14, 15, 16, 17)
    GLCDInit()
    GLCDDisplayClear()
    GLCDBox(0, 0, 127, 63)
    GLCDLine(0, 15, 127, 15)

    try:
        while True:
            time.sleep(1.0)
    except KeyboardInterrupt:
        GLCDDisplayClear()
        GPIO.cleanup()
def __main():
    PinsInit(20, 7, 8, 9, 18, 19, 10, 11, 12, 13, 14, 15, 16, 17)
    GLCDInit()
    GLCDDisplayClear()
    GLCDBox(0, 0, 127, 63)
    GLCDLine(0, 15, 127, 18)

    try:
        while True:
            time.sleep(1.0)
    except KeyboardInterrupt:
        GLCDDisplayClear()
        GPIO.cleanup()

【ラズパイ】【GLCD】画面に四角の線を引く※追記あり

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

最新ソースをgitHubにアップしました。

https://github.com/takishita2nd/GLCD

画面に四角を書きます。

これもこちらのAudiunoのサンプルコードをPython用に書き換えた物です。

def GLCDBox(Xp0, Yp0, Xp1, Yp1):
    for i in range(Xp0, Xp1 + 1):
        GLCDPutPixel(i, Yp0)
        GLCDPutPixel(i, Yp1)
    for i in range(Yp0 + 1, Yp1):
        GLCDPutPixel(Xp0, i)
        GLCDPutPixel(Xp1, i)

def GLCDPutPixel(Xp, Yp):
    #ラインの選択処理
    L = 1 << (Yp % 8)
    #LCDに表示するアドレスの位置をセットする
    SetLocation(Xp, Yp)
    #LCD画面の現在表示内容に指定位置のビット(L)をON(XOR)させ、そのデータをLCDに送る
    L = ReadData() | L
    SetAddress(SetCol)
    WriteData(L)

def ReadData():
    #データピンを入力に設定
    for i in range(8):
        GPIO.setup(DATA_p[i], GPIO.IN)
    #読み込みモードにする
    GPIO.output(RW_p, GPIO.HIGH)
    GPIO.output(RS_p, GPIO.HIGH)
    #データを読み込む
    GPIO.output(E_p, GPIO.HIGH)
    GPIO.output(E_p, GPIO.LOW)
    ans = 0
    GPIO.output(E_p, GPIO.HIGH)
    for i in range(8):
        ans = ans | (GPIO.input(DATA_p[i]) << i)
    GPIO.output(E_p, GPIO.LOW)
    #書き込みモードにする
    GPIO.output(RW_p, GPIO.LOW)
    #データピンを出力に設定
    for i in range(8):
        GPIO.setup(DATA_p[i], GPIO.OUT)
    return ans

ピクセルを置く際には、一度表示させているデータを取得し、そのデータのORをとって表示させます。

※サンプルではここがXORになっていたけど、どっちが正しいんだろ?

READする時は、ピンのモードを出力から入力に切り替える必要があります。

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

んんー?なんかおかしいぞ?

もしかして、信号の読み取りでおかしなデータを読み込んでいるんじゃ無いか?

タイミングチャートを確認する。

ここを確認すると、E信号のHIGHにしてからLOWにするまでの時間(tWH、tWL)が最低450nsと書かれています。

なので、この間に0.5ms(500ns)をsleepを入れてみます。※追記あり

EWAIT = 0.0005
def ReadData():
    #データピンを入力に設定
    for i in range(8):
        GPIO.setup(DATA_p[i], GPIO.IN)
    #読み込みモードにする
    GPIO.output(RW_p, GPIO.HIGH)
    GPIO.output(RS_p, GPIO.HIGH)
    #データを読み込む
    GPIO.output(E_p, GPIO.HIGH)
    time.sleep(EWAIT) #★
    GPIO.output(E_p, GPIO.LOW)
    ans = 0
    GPIO.output(E_p, GPIO.HIGH)
    time.sleep(EWAIT) #★
    for i in range(8):
        ans = ans | (GPIO.input(DATA_p[i]) << i)
    GPIO.output(E_p, GPIO.LOW)
    #書き込みモードにする
    GPIO.output(RW_p, GPIO.LOW)
    #データピンを出力に設定
    for i in range(8):
        GPIO.setup(DATA_p[i], GPIO.OUT)
    return ans

def command(value, mode):
    GPIO.output(RS_p, mode)
    for i in range(8):
        GPIO.output(DATA_p[i], (value >> i) & 0x01)
    GPIO.output(E_p, GPIO.HIGH)
    time.sleep(EWAIT) #★
    GPIO.output(E_p, GPIO.LOW)

モッサリしてる。

もう一度タイミングチャートを確認。

tNはEをHIGHにしてからデータ端子0-7が出力されるまでの時間です。

この表を見れば最大25nsと書いてあります。

なので、E信号のsleep時間を0.03ms(30ns)まで縮めてみましょう。※追記あり

EWAIT = 0.00003

だいぶマシになったのではないでしょうか

動作も安定してオールOKです。

いやーすごい濃いことやってるなー

※追記

ああああ!

単位間違えてるじゃん!

これじゃあμsじゃん!

正しくnsに修正。

EWAIT = 0.0000005

【C#】【ピクロス】【ALTSEED】解析パターン11

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

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

https://github.com/takishita2nd/Picross

次はこちらの問題を解いてみます。

解析実行。

この状態で止まってしまいましたね。

また新しい解析パターンを追加する必要があるようです。

ここに注目してみました。

この列では4が一番大きい数字で、すでに4マス塗られていますので、この4はすでに確定します。

なので、この4は解析済みにし、両端をマスクする、というロジックが必要です。

これを実装します。

考え方は、

  1. 行、列から連続する塗られたマスを取得する
  2. その中から最大の物を取得する
  3. 数字の中で一番大きい物を取得する
  4. 取得した連続するマスの数が、一番多き数字と一致するかどうかを確認する
  5. 一致するならば、その数字を解析済みにし、塗られた連続するマスの両端をマスクする

コードはこうなりました。

        // 解析パターンその11
        // すでに塗っている場所を解析済みにする
        private void pattern11()
        {
            // Row
            pattern11Row();
            // Col
            pattern11Col();
        }

        private void pattern11Row()
        {
            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].IsPainted() == true)
                        {
                            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++;
                        rowlist.AnalyzeDatas.Reverse();
                        continue;
                    }
                }

                List<BitmapData> targetDataList = null;
                // 最大の物を取得する
                foreach (var datalist in data)
                {
                    if(targetDataList == null)
                    {
                        targetDataList = datalist;
                    }
                    else if(targetDataList.Count < datalist.Count)
                    {
                        targetDataList = datalist;
                    }
                }

                AnalyzeData targetRowList = null;
                foreach(var rowdata in rowlist.AnalyzeDatas)
                {
                    if(targetRowList == null)
                    {
                        targetRowList = rowdata;
                    }
                    else if(targetRowList.Value < rowdata.Value)
                    {
                        targetRowList = rowdata;
                    }

                }

                if(targetDataList.Count == targetRowList.Value)
                {
                    //解析済みにする
                    targetRowList.Analyzed();
                    if(targetDataList[0].Col > 0)
                    {
                        if (_bitmapData[targetDataList[0].Row, targetDataList[0].Col - 1].IsValid() == false)
                        {
                            _bitmapData[targetDataList[0].Row, targetDataList[0].Col - 1].Mask();
                        }
                    }
                    if (targetDataList[targetDataList.Count - 1].Col < colNumbers.Count - 1)
                    {
                        if (_bitmapData[targetDataList[targetDataList.Count - 1].Row, targetDataList[targetDataList.Count - 1].Col + 1].IsValid() == false)
                        {
                            _bitmapData[targetDataList[targetDataList.Count - 1].Row, targetDataList[targetDataList.Count - 1].Col + 1].Mask();
                        }
                    }
                }

                rowlist.AnalyzeDatas.Reverse();
                row++;
            }
        }

        private void pattern11Col()
        {
            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].IsPainted() == true)
                        {
                            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++;
                        collist.AnalyzeDatas.Reverse();
                        continue;
                    }
                }

                List<BitmapData> targetDataList = null;
                // 最大の物を取得する
                foreach (var datalist in data)
                {
                    if (targetDataList == null)
                    {
                        targetDataList = datalist;
                    }
                    else if (targetDataList.Count < datalist.Count)
                    {
                        targetDataList = datalist;
                    }
                }

                AnalyzeData targetColList = null;
                foreach (var coldata in collist.AnalyzeDatas)
                {
                    if (targetColList == null)
                    {
                        targetColList = coldata;
                    }
                    else if (targetColList.Value < coldata.Value)
                    {
                        targetColList = coldata;
                    }

                }

                if (targetDataList.Count == targetColList.Value)
                {
                    //解析済みにする
                    targetColList.Analyzed();
                    if (targetDataList[0].Row > 0)
                    {
                        if (_bitmapData[targetDataList[0].Row - 1, targetDataList[0].Col].IsValid() == false)
                        {
                            _bitmapData[targetDataList[0].Row - 1, targetDataList[0].Col].Mask();
                        }
                    }
                    if (targetDataList[targetDataList.Count - 1].Row < colNumbers.Count - 1)
                    {
                        if (_bitmapData[targetDataList[targetDataList.Count - 1].Row + 1, targetDataList[targetDataList.Count - 1].Col].IsValid() == false)
                        {
                            _bitmapData[targetDataList[targetDataList.Count - 1].Row + 1, targetDataList[targetDataList.Count - 1].Col].Mask();
                        }
                    }
                }

                collist.AnalyzeDatas.Reverse();
                col++;
            }
        }

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

まだ完全では無いですが、少し進みましたね。