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

【北海道大戦】バトル処理を実装する。

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

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

https://github.com/takishita2nd/HokkaidoWar

前回は対戦相手を選択する処理を作成したので、

今回は実際にバトルを行う処理を作成します。

    class Battle
    {
        public void NextTurn()
        {
            if (lastDeffece != null)
            {
                lastDeffece.ClearPaint();
            }
            if (lastAttack != null)
            {
                lastAttack.ClearPaint();
            }

            var targets = _cities[cityCnt].GetLinkedCities();
            var r = Singleton.GetRandom();
            int targetIdx = r.Next(0, targets.Count + 1);
            lastAttack = _cities[cityCnt];
            lastAttack.PaintAttackColor();

            var info = Singleton.GetGameProcessInfomation();
            if(targetIdx >= targets.Count)
            {
                info.ShowText(lastAttack.GetPosition(), string.Format("{0} turn {1} / {2} {3}",
                    turn, cityCnt + 1, _cities.Count, lastAttack.Name));
            }
            else
            {
                lastDeffece = targets[targetIdx];
                lastDeffece.PaintDeffenceColor();
                float attack = lastAttack.Population * (float)(r.Next(5, 30) / 10.0);
                float deffence = lastDeffece.Population * (float)(r.Next(5, 30) / 10.0);
                if(attack > deffence)
                {
                    info.ShowText(lastAttack.GetPosition(), string.Format("{0} turn {1} / {2} {3}\r\ntarget {4} \r\n{5} vs {6}\r\nwin",
                        turn, cityCnt + 1, _cities.Count, lastAttack.Name, lastDeffece.Name, (int)attack, (int)deffence));
                    lastAttack.CombinationCity(lastDeffece);
                    _cities.Remove(lastDeffece);
                    lastDeffece = null;
                }
                else
                {
                    info.ShowText(lastAttack.GetPosition(), string.Format("{0} turn {1} / {2} {3}\r\ntarget {4} \r\n{5} vs {6}\r\nlose",
                        turn, cityCnt + 1, _cities.Count, lastAttack.Name, lastDeffece.Name, (int)attack, (int)deffence));
                }
            }

            cityCnt++;
            if(cityCnt >= _cities.Count)
            {
                _cities = cityRandomReplace(_cities);
                cityCnt = 0;
                turn++;
            }
        }
    class City
    {
        public List<Map> GetMaps()
        {
            return _maps;
        }

        public void CombinationCity(City lose)
        {
            addMaps(lose.GetMaps());
            _population += lose.Population;
        }

        private void addMaps(List<Map> maps)
        {
            foreach(var m in maps)
            {
                m.SetCity(this);
            }
            _maps.AddRange(maps);
        }

とりあえず、0.5倍~3倍の乱数で戦力値に補正をかけて勝敗を決めます。

そして、攻撃側が勝利した場合は防御側を吸収合併します。

これで一通り実装は完了したので、実際に大戦を実行しましょう。

札幌の一人勝ちでした。

もうちょっとパラメータを調整しないといけないですね。

【ラズパイ】スイッチ操作を割り込みで行う。

最新ソースはこちら。

https://github.com/takishita2nd/RaspiDisplayMonitor

以前使用していた12個のスイッチは3つの信号と4つの制御線の組み合わせで検出していましたが、

いまはスイッチは1個なので、エッジの割り込みで操作できるはずです。

スレッドが一つ無くなるので、ラズパイZeroでも動作が軽くなると思います。

import RPi.GPIO as GPIO
import time
import datetime
import calendar
import GLCD
import AM2320
import Weather

sw = False

def __main__():
    global sw
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(22,GPIO.IN) 
    GPIO.add_event_detect(22, GPIO.FALLING, callback=callback, bouncetime=300)
    GLCD.PinsInit(20, 7, 8, 9, 18, 19, 10, 11, 12, 13, 14, 15, 16, 17)
    GLCD.GLCDInit()
    GLCD.GLCDDisplayClear()

    roop = 10 * 60 * 60
    mode = 1
    try:
        while True:
            if sw == True:
                GLCD.GLCDDisplayClear()
                mode += 1
                if mode > 3:
                    mode = 1
                sw = False

            if mode == 1:
                if roop >= 10 * 60 * 60:
                    GLCD.GLCDDisplayClear()
                    Weather.RequestAPI()
                    weather = Weather.GetWeather()
                    temp = Weather.GetTemp()
                    roop = 0

                GLCD.GLCDPuts(1, 0, "Date :")
                GLCD.GLCDPuts(5, 8, datetime.datetime.now().strftime('%Y:%m:%d %A '))
                GLCD.GLCDPuts(1, 16, "Weather :")
                GLCD.GLCDPuts(10,24, weather)
                GLCD.GLCDPuts(10,32, "Temp : " + format(temp) + 'C')
                GLCD.GLCDPuts(1, 40, "Time : " + datetime.datetime.now().strftime('%H:%M'))
                GLCD.GLCDPuts(1, 48, "Humidity    : " + AM2320.GetHum() + '%')
                GLCD.GLCDPuts(1, 56, "Temperature : " + AM2320.GetTemp() + 'C')

                roop += 1
            elif mode == 2:
                cal = calendar.month(datetime.datetime.now().year, datetime.datetime.now().month)
                cals = cal.split("\n")
                y = 0
                for c in cals:
                    GLCD.GLCDPuts(1, y, c)
                    y += 8
            
            elif mode == 3:
                GLCD.drowMashiro()

            time.sleep(1)
    except KeyboardInterrupt:
        GLCD.GLCDDisplayClear()
        GPIO.cleanup()

def callback(channel):
    global sw
    sw = True

__main__()

GPIO.add_event_detect()で割り込み処理の登録を行います。

監視するGPIOの番号、エッジ(HIGH/LOW)、コールバック関数、検出間隔指定します。

上の処理では、GPIO22が下りエッジを検出したらcallback()が実行されます。

スイッチ検出がかなりスムーズになりました。

【Laravel】【ダイエット支援】【食事管理】入力ダイアログを作成する

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

最新ソースファイルはこちら(gitHub)

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

入力ダイアログの画面を作成していきます。

ベースは体重管理で作成したものを使用します。

実際の処理は後で作成します。

EatingInputDialogComponent.vue

<template>
    <div>
        <div id="overlay" v-show="show">
            <div id="content">
                <p v-if="error_flg == true" class="error">
                    <ui>
                        <li v-for="error in errors">{{ error }}</li>
                    </ui>
                </p>
                <table class="edit">
                    <tbody>
                        <tr>
                            <td>品名</td>
                            <td><input type="text" v-model="contents.item" /></td>
                        </tr>
                        <tr>
                            <td>タンパク質</td>
                            <td><input type="number" v-model="contents.protein" /></td>
                        </tr>
                        <tr>
                            <td>脂質</td>
                            <td><input type="number" v-model="contents.riqid" /></td>
                        </tr>
                        <tr>
                            <td>炭水化物</td>
                            <td><input type="number" v-model="contents.carbo" /></td>
                        </tr>
                        <tr>
                            <td>カロリー</td>
                            <td><input type="number" v-model="contents.calorie" /></td>
                        </tr>
                    </tbody>
                </table>
                <p id="command">
                    <button @click="clickAdd">入力</button>
                    <button @click="closeModal">閉じる</button>
                </p>
            </div>
        </div>
    </div>
</template>
<script>
export default {
    props: ['show'],
    data() {
        return {
            errors: [],
            error_flg: [],
            param: {},
            contents: {
                item: "",
                protein: "",
                riqid: "",
                carbo: "",
                calorie: "",
            },
        };
    },
    created: function() {
    },
    methods: {
        clickAdd: function() {
        },
        closeModal: function() {
            this.$parent.showInputDialogContent = false;
        },
        clear: function() {
            this.contents.item = "";
            this.contents.protein = "";
            this.contents.riqid = "";
            this.contents.carbo = "";
            this.contents.calorie = "";
            this.error_flg = false;
            this.errors = [];
        }
    }
}
</script>
app.js

Vue.component('eating-input-dialog-component', require('./components/Eating/EatingInputDialogComponent.vue'));
EatingDashboardComponent.vue

<template>
    <div>
        <div class="dashboard">
            <div class="chart">
                <canvas id="eating"></canvas>
            </div>
            <div class="command">
                <ul>
                    <li><a @click="onClickPrev">prev</a></li>
                    <li><a @click="onClickNext">next</a></li>
                </ul>
                <ul>
                    <li><a @click="onClickInput">クイック入力</a></li>
                    <li><a href="">詳細</a></li>
                </ul>
            </div>
        </div>
        <eating-input-dialog-component :show="showInputDialogContent" @update="invokeUpdateList"></eating-input-dialog-component>
    </div>
</template>

<script>
export default {
    data() {
        return {
            showInputDialogContent: false,
        };
    },
    methods: {
        onClickInput: function() {
            this.showInputDialogContent = true;
        },
        invokeUpdateList: function() {
            //this.graphUpdate(this.graphNum);
        },
    }
}
</script>

【ラズパイ】pythonプログラムをサービスで実行する

ラズパイZeroで作成したpythonプログラムをサービスに登録して、起動と同時に実行するように設定します。

以下のファイルを作成

pimonitor.service

[Unit]
Description=PIMonitor
After=syslog.target

[Service]
Type=simple
WorkingDirectory=/opt/pimonitor
ExecStart=/usr/bin/python3 main.py
TimeoutStopSec=5
StandardOutput=null

[Install]
WantedBy = multi-user.target

このファイルを/etc/systemd/system/配下にコピー

$ cp pimonitor.service /etc/systemd/system

※ /opt/pimonitor配下にソースファイル全て揃っている前提。

パーミッションの設定でrootでも実行できようにしておく

$ chmod 777 *

サービス開始

$ sudo systemctl start pimonitor.service

サービス登録

$ sudo systemctl enable pimonitor.service

【北海道大戦】ランダムで隣接する都市を選択する

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

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

https://github.com/takishita2nd/HokkaidoWar

今回はバトルを仕掛ける市町村を選択する処理を実装します。

自分のターンが回ってきた市町村から、隣接する市町村をランダムで選択し、攻撃を仕掛ける、という感じにしたいと思います。

ただし、何もしない(攻撃を仕掛けない)ケースもあるものとします。

さらに、攻撃側の市町村と防御側の市町村が分かるように色分けしましょうか。

    class City
    {

        public void PaintAttackColor()
        {
            var color = new asd.Color(255, 0, 0);
            foreach(var m in _maps)
            {
                m.SetColor(color);
            }
        }

        public void PaintDeffenceColor()
        {
            var color = new asd.Color(0, 0, 255);
            foreach (var m in _maps)
            {
                m.SetColor(color);
            }
        }

        public void ClearPaint()
        {
            foreach (var m in _maps)
            {
                m.SetColor(_color);
            }
        }
    class Battle
    {
        public void NextTurn()
        {
            if(prevAttack != null)
            {
                prevAttack.ClearPaint();
            }
            if(prevDeffece != null)
            {
                prevDeffece.ClearPaint();
            }

            var targets = _cities[cityCnt].GetLinkedCities();
            var r = Singleton.GetRandom();
            int targetIdx = r.Next(0, targets.Count + 1);
            prevAttack = _cities[cityCnt];
            prevAttack.PaintAttackColor();

            var info = Singleton.GetGameProcessInfomation();
            if(targetIdx >= targets.Count)
            {
                info.ShowText(prevAttack.GetPosition(), string.Format("{0} turn {1} / {2} {3}",
                    turn, cityCnt + 1, _cities.Count, prevAttack.Name));
            }
            else
            {
                prevDeffece = targets[targetIdx];
                prevDeffece.PaintDeffenceColor();
                info.ShowText(prevAttack.GetPosition(), string.Format("{0} turn {1} / {2} {3}\r\ntarget {4}",
                    turn, cityCnt + 1, _cities.Count, prevAttack.Name, prevDeffece.Name));
            }

            cityCnt++;
            if(cityCnt >= _cities.Count)
            {
                _cities = cityRandomReplace(_cities);
                cityCnt = 0;
                turn++;
            }
        }

隣接する都市を取得する処理は今までに作成しているので、メソッド1つで取り出せます。

その中から乱数で1つ都市を選択します。

ただし、何もしないターンもあるので、乱数の範囲を大きめにとって、配列より大きければ、何もしない、と扱います。

【ラズパイ】【いろいろ計測モニター】実際に組んでみる。

今日は9時間頑張りました。

実際にこの回路を組んでみました。

材料はこちらの記事にあります。

結果はこうなりました。

挫けた。

そもそも、

このラズパイZero用の基板に全部盛り込もうとしたのが間違い。

かなり難易度の高いはんだ付け作業になりました。

また、穴と穴が繋がっていない(ユニバーサル基板とはそういうもの)ので、その線をつなぐのも苦労しました。

さらに、


GLCD側のピッチ幅がラズパイZero側の基板より小さい事が発覚。

これがさらにはんだ付けの難易度を上げました。

はんだの熱で基板と部品が若干溶けてました。

このままじゃ悔しいので、ブレッドボードに回路を作り直して、ラズパイZeroで動かしました。

今回はこれで勘弁してください。

9時間頑張ったんだよ・・・

でもラズパイZeroで動かしているので、長時間稼働していても全然暑くないです。

でも性能は明らかに劣っているからモッサリしているけどね。

損傷して使い物にならなくなってしまいました。

残念ながらゴミ箱行きです。

【LARAVEL】【ダイエット支援】ダッシュボードに食事管理を追加する

前回までの状況はこちら

最新ソースファイルはこちら(gitHub)

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

ダッシュボード画面に、

この画面の追加します。

ベースは体重管理のものを流用できるので、さくっと作成しました。

EatingDashboardComponent.vue

<template>
    <div>
        <div class="dashboard">
            <div class="chart">
                <canvas id="eating"></canvas>
            </div>
            <div class="command">
                <ul>
                    <li><a @click="onClickPrev">prev</a></li>
                    <li><a @click="onClickNext">next</a></li>
                </ul>
                <ul>
                    <li><a @click="onClickInput">クイック入力</a></li>
                    <li><a href="">詳細</a></li>
                </ul>
            </div>
        </div>
    </div>
</template>

<script>
export default {
    data() {
        return {
        };
    },
    created: function() {
    },
    mounted: function() {
    },
    methods: {
        onClickNext: function() {
        },
        onClickPrev: function() {
        },
        onClickInput: function() {
        },
    }
}
</script>
app.js

Vue.component('eating-dashboard-component', require('./components/Eating/EatingDashboardComponent.vue'));
home.blade.php

@extends('layouts.app')

@section('content')
<div class="container">
    <div class="row">
        <div class="col-md-8 col-md-offset-2">
            <div class="panel panel-default">
                <div class="panel-heading">Dashboard</div>

                <div class="panel-body">
                    @if (session('status'))
                        <div class="alert alert-success">
                            {{ session('status') }}
                        </div>
                    @endif

                    <weight-dashboard-component></weight-dashboard-component>
                    <p></p>
                    <eating-dashboard-component></eating-dashboard-component>
                </div>
            </div>
        </div>
    </div>
</div>
@endsection

【ラズパイ】【いろいろ計測モニター】

どうせならきちんとして使える物を作りたい。

前回まではラズパイ4(2GB)を使用しましたが、これって、ファンを使わないと発熱がやばいらしいですね。

なので、消費電力が少なくて、発熱も少ないラズパイZERO HWを買いました。

使用する部品も買いました。

MicroUSBのACアダプター

MicroSDカード(16GB)

8GBでも十分だったんだけどね。

これらは今まで使用していた部品と同じです。

ラズパイZero用の基板です。

これにピンヘッダを取り付けて、基板と本体を取り外しできるようにします。

スイッチに使用する抵抗とダイオード。

(たぶん)同じ物を購入しました。

それらをつなぐジャンパ線です。

こんな感じで回路を組もうと思ってます。

結構ごちゃごちゃしてる。

まぁ、ピンの位置とか実際の位置に合わせて書いているので仕方が無い。

問題はスイッチが正しく動くかどうか。

前回は12個のスイッチでしたが、今回は1個のスイッチを使います。それに合わせて同じような回路を組んでいます。

ちゃんと機能するか、ブレッドボードに簡単に組んでみました。

上手くいけば、スイッチ押下時にインプット信号がHIGH→LOWになるはずです。

import RPi.GPIO as GPIO
import time

GPIO.setmode(GPIO.BCM)
GPIO.setup(21, GPIO.IN)
swState = 0

try:
    while True:
        if GPIO.input(21) == 0 and swState == 0:
            time.sleep(0.05)
            if GPIO.input(21) == 0:
                print("ON")
                swState = 1
        elif GPIO.input(21) == 1 and swState == 1:
            print("OFF")
            swState = 0
        time.sleep(0.005)

except KeyboardInterrupt:
    GPIO.cleanup()
$ python3 test.py 
ON
OFF
ON
OFF
ON
OFF
ON
OFF
ON
OFF

うん、うまく動いているようです。

じゃあ、制作開始といきますか。

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

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

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

https://github.com/takishita2nd/Picross

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

実行結果はこちら

惜しい。

多分、次やらなくちゃ行けないのはここだろうな。

ここは2,2,5とすでに塗られているので、空いている空白はマスクで確定されます。

ちょっと解析パターン11と似ていますが、こんなコードを組んでみました。

        // 解析パターンその15
        // 数字と空いているマスを照合して塗る
        private void pattern15()
        {
            // Row
            pattern15Row();
            // Col
            pattern15Col();
        }

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

                // 有効な数字を取り出す
                List<AnalyzeData> aData = new List<AnalyzeData>();
                foreach(var data in rowlist.AnalyzeDatas)
                {
                    if (data.IsAnalyzed())
                    {
                        continue;
                    }
                    aData.Add(data);
                }

                // 対象となるマスを抽出する
                List<List<BitmapData>> bitmapLists = extractTargetBitmapListsCol(row);
                bitmapLists.Reverse();

                // 数字とマスを照合する
                if (bitmapLists.Count != aData.Count)
                {
                    row++;
                    continue;
                }

                for(int i = 0; i < aData.Count; i++)
                {
                    int count = 0;
                    // 塗られているマスを数える
                    foreach (var b in bitmapLists[i])
                    {
                        if (b.IsPainted())
                        {
                            count++;
                        }
                    }
                    // 塗られているマスと数字が一定している場合は空きマスをマスクする
                    if(aData[i].Value == count)
                    {
                        foreach(var b in bitmapLists[i])
                        {
                            if(b.IsValid() == false)
                            {
                                b.Mask();
                            }
                        }
                    }
                }
                row++;
            }
        }

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

                // 有効な数字を取り出す
                List<AnalyzeData> aData = new List<AnalyzeData>();
                foreach (var data in collist.AnalyzeDatas)
                {
                    if (data.IsAnalyzed())
                    {
                        continue;
                    }
                    aData.Add(data);
                }

                // 対象となるマスを抽出する
                List<List<BitmapData>> bitmapLists = extractTargetBitmapListsRow(col);
                bitmapLists.Reverse();

                // 数字とマスを照合する
                if (bitmapLists.Count != aData.Count)
                {
                    col++;
                    continue;
                }

                for (int i = 0; i < aData.Count; i++)
                {
                    int count = 0;
                    // 塗られているマスを数える
                    foreach (var b in bitmapLists[i])
                    {
                        if (b.IsPainted())
                        {
                            count++;
                        }
                    }
                    // 塗られているマスと数字が一定している場合は空きマスをマスクする
                    if (aData[i].Value == count)
                    {
                        foreach (var b in bitmapLists[i])
                        {
                            if (b.IsValid() == false)
                            {
                                b.Mask();
                            }
                        }
                    }
                }
                col++;
            }
        }

んー思った以上に進んでいないぞ。

少々時間がかかりそうです。

もう少し調べてみます。

【北海道大戦】各市町村の行動をランダムで回す

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

https://github.com/takishita2nd/HokkaidoWar

早速バトル処理を作成していきます。

しかし、ただループさせるだけだと途中経過が全く見えないので、

マウスをクリックすると次の行動を実行する、という形にしたいと思います。

そして、ターンの最初には、市町村の行動順をランダムに並べ替えます。

まずは、市町村を並び替える処理を作成。

    class Battle
    {
        private List<City> cityRandomReplace(List<City> beforeCities)
        {
            var r = Singleton.GetRandom();
            List<City> afterCities = new List<City>();
            int max = beforeCities.Count;
            for (int i = 0; i < max; i++)
            {
                int index = r.Next(0, beforeCities.Count - 1);
                afterCities.Add(beforeCities[index]);
                beforeCities.RemoveAt(index);
            }

            return afterCities;
        }

乱数でindexの値を振り、そのindexの都市を新しいListに追加して、古いリストから削除する、という処理を繰り返す事で並べ替えできます。

これを使用してマウスクリック処理を作成しいます。

    class HokkaidoWar
    {
        public void Run()
        {
            _battle = new Battle(cities);

            while (asd.Engine.DoEvents())
            {
                if (asd.Engine.Mouse.LeftButton.ButtonState == asd.ButtonState.Push)
                {
                    _battle.NextTurn();
                }
                asd.Engine.Update();
            }
            asd.Engine.Terminate();
    class Battle
    {
        private List<City> _cities = null;
        private int turn;
        private int cityCnt;
        public Battle(List<City> cities)
        {
            turn = 1;
            cityCnt = 0;
            _cities = new List<City>();
            foreach(var c in cities)
            {
                _cities.Add(c);
            }
            _cities = cityRandomReplace(_cities);
        }

        public void NextTurn()
        {
            cityCnt++;
            if(cityCnt >= _cities.Count)
            {
                _cities = cityRandomReplace(_cities);
                cityCnt = 0;
                turn++;
            }

            var info = Singleton.GetGameProcessInfomation();
            info.ShowText(_cities[cityCnt].GetPosition(), string.Format("{0} turn {1} / {2} {3}", turn, cityCnt, _cities.Count, _cities[cityCnt].Name));
        }

マウスをクリックする度に、NextTurn()が実行され、順番が回ってきた市町村の名前を表示します。

そして、一巡したらターンを更新し、市町村を並べ替えて処理を続けます。

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