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

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

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

前回まではラズパイ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()が実行され、順番が回ってきた市町村の名前を表示します。

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

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

【Laravel】【ダイエット支援】食事管理データベースを作成する

食事管理機能の作成に着手します。

まずはデータベースの作成。

マイグレーションを作成します。

$ php artisan make:migration create_eating_management
class CreateEatingManagement extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('eating_managements', function (Blueprint $table) {
            $table->increments('id');
            $table->date('date');
            $table->text('item');
            $table->integer('protein');
            $table->integer('ripid');
            $table->integer('carbo');
            $table->integer('calorie');
            $table->timestamps();
            $table->engine = 'InnoDB';
            $table->charset = 'utf8mb4';
            $table->collation = 'utf8mb4_unicode_ci';
        });

        Schema::create('eating_management_user', function (Blueprint $table) {
            $table->integer('user_id')
                  ->foreign('user_id')
                  ->references('id')->on('users')
                  ->onDelete('cascade');
            $table->integer('eating_management_id')
                  ->foreign('eating_management_id')
                  ->references('id')->on('eating_managements')
                  ->onDelete('cascade');
            $table->engine = 'InnoDB';
            $table->charset = 'utf8mb4';
            $table->collation = 'utf8mb4_unicode_ci';
        });

        Schema::create('timezones', function (Blueprint $table) {
            $table->increments('id');
            $table->text('zone');
            $table->timestamps();
            $table->engine = 'InnoDB';
            $table->charset = 'utf8mb4';
            $table->collation = 'utf8mb4_unicode_ci';
        });

        Schema::create('eating_management_timezone', function (Blueprint $table) {
            $table->integer('eating_management_id')
                  ->foreign('eating_management_id')
                  ->references('id')->on('eating_managements')
                  ->onDelete('cascade');
            $table->integer('timezone_id')
                  ->foreign('timezone_id')
                  ->references('id')->on('timezones')
                  ->onDelete('cascade');
            $table->engine = 'InnoDB';
            $table->charset = 'utf8mb4';
            $table->collation = 'utf8mb4_unicode_ci';
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('eating_management_timezone');
        Schema::dropIfExists('timezones');
        Schema::dropIfExists('eating_management_user');
        Schema::dropIfExists('eating_managements');
    }
}

必要な項目は、日付、時間帯、品目、タンパク質、脂質、炭水化物、カロリー。

時間帯は別テーブルで定義して、朝、昼、夜、間食から選択します。

時間帯は固定値なので、Seederで作成します。

$ php artisan make:seeder TimezoneSeeder
class TimezoneSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        DB::table('timezones')->insert(['zone' => '朝',]);
        DB::table('timezones')->insert(['zone' => '昼',]);
        DB::table('timezones')->insert(['zone' => '夜',]);
        DB::table('timezones')->insert(['zone' => '間食',]);
    }
}

これらを実行して適用させます。

$ composer dump-autoload
Generating optimized autoload files
> Illuminate\Foundation\ComposerScripts::postAutoloadDump
> @php artisan package:discover
Discovered Package: fideloper/proxy
Discovered Package: laravel/tinker
Discovered Package: nesbot/carbon
Package manifest generated successfully.
$ php artisan migrate
Migrating: 2020_07_10_092328_create_eating_management
Migrated:  2020_07_10_092328_create_eating_management (0.07 seconds)
$ php artisan db:seed --class=TimezoneSeeder
Database seeding completed successfully.

作成時間にNULLって入っているけど、まあいいか。

【ラズパイ】これまでやってきたことを組み合わせる。

ここまでやってきたことを全て組み合わせます。

GLCDに表示するのは、

  • 時刻、天気、温度、湿度
  • カレンダー
  • 倉田ましろの絵

この3つの表示を、スイッチの1番を押すことで切り替えます。

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


def __main__():
    GPIO.setmode(GPIO.BCM)
    GLCD.PinsInit(20, 7, 8, 9, 18, 19, 10, 11, 12, 13, 14, 15, 16, 17)
    GLCD.GLCDInit()
    GLCD.GLCDDisplayClear()

    SW.PinsInit(21, 22, 23, 24, 25, 26, 27)
    SW.Start()

    roop = 10 * 60 * 60
    mode = 1
    try:
        while True:
            key = SW.GetKey()
            if key == "1":
                GLCD.GLCDDisplayClear()
                mode += 1
                if mode > 3:
                    mode = 1

            if mode == 1:
                if roop >= 10 * 60 * 60:
                    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:%S'))
                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(0.1)
    except KeyboardInterrupt:
        SW.Stop()
        GLCD.GLCDDisplayClear()
        GPIO.cleanup()

__main__()

スイッチ押下でmodeの値が変化し、表示処理の内容を変えています。

なかなか上手くいったので、これで完成品を作ってみようか。

ラズパイ遊びの集大成です。

【C#】【ピクロス】【ALTSEED】自動テスト機能を実装する。

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

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

https://github.com/takishita2nd/Picross

さて、いよいよ、自動テストを作っていきますか。

前回で問題データと解答データを用意したので、

問題データの読み込み→解析→回答データと照合→結果出力

こんな感じの流れでできると思います。

という訳で実装。

    class AutoTest
    {
        private const string QuestionFolder = "q";
        private const string AnswerFolder = "a";
        private const string ResultFolder = "r";
        private const string QuestionFile = "save";
        private const string AnswerFile = "output";
        private const string ResultFile = "result";

        public AutoTest()
        {

        }

        public void Run()
        {
            var files = Directory.EnumerateFiles(QuestionFolder, "*");
            foreach(var file in files)
            {
                string answerFile = file.Replace(QuestionFolder, AnswerFolder).Replace(QuestionFile, AnswerFile);
                string resultFile = file.Replace(QuestionFolder, ResultFolder).Replace(QuestionFile, ResultFile);
                if (File.Exists(resultFile))
                {
                    File.Delete(resultFile);
                }

                if (File.Exists(answerFile))
                {
                    List<List<NumberSquare>> rowNumberSquare = new List<List<NumberSquare>>();
                    List<List<NumberSquare>> colNumberSquare = new List<List<NumberSquare>>();
                    FileAccess.Load(file, ref rowNumberSquare, ref colNumberSquare);
                    PicrossAnalyze picross = new PicrossAnalyze(rowNumberSquare, colNumberSquare);
                    var ret = picross.Run();
                    string answer = string.Empty;
                    for(int r = 0; r < rowNumberSquare.Count; r++)
                    {
                        for(int c = 0; c < colNumberSquare.Count; c++)
                        {
                            if (ret[r, c].IsPainted())
                            {
                                answer += "1";
                            }
                            else
                            {
                                answer += "0";
                            }
                        }
                        answer += "\r\n";
                    }
                    string compare = FileAccess.AnswerLoad(answerFile);
                    string result;
                    if(compare.CompareTo(answer) == 0)
                    {
                        result = "OK";
                    }
                    else
                    {
                        result = answer;
                    }
                    using (var stream = new StreamWriter(resultFile, true))
                    {
                        stream.Write(result);
                    }
                }
            }
        }
    }
    class PicrossUI
    {
        public void Run()
        {

            // テストボタン
            var testButton = new Button(10, 170, "テスト");
            testButton.Show();
            asd.Engine.AddObject2D(testButton.getBackTexture());
            asd.Engine.AddObject2D(testButton.getTextObject());
            buttons.Add(testButton);
            testButton.SetAction(() =>
            {
                var test = new AutoTest();
                test.Run();
            });

Directory.EnumerateFiles()で問題ファイルの一覧を取得し、それをforeachで回します。

問題ファイルからデータを取り出し、ピクロス解析処理にかけます。

その結果を前回のような0と1の文字列に変換し、CompareTo()で回答結果との照合を行います。

結果、問題無ければOK、問題があれば解析結果をresultファイルに出力します。

これでテストも楽になる!

【ラズパイ】【GLCD】カレンダーを表示する

GLCDにカレンダーを表示させたいと思います。

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


def __main__():
    GPIO.setmode(GPIO.BCM)
    GLCD.PinsInit(20, 7, 8, 9, 18, 19, 10, 11, 12, 13, 14, 15, 16, 17)
    GLCD.GLCDInit()
    GLCD.GLCDDisplayClear()

    try:
        while True:
            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

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

__main__()

calendar.month(year, month)で現在月のカレンダーを文字列で出力されます。

これは改行文字を使って整形されているので、改行文字で分割し一行ずつ表示位置を変えながら表示させています。

出来れば今日の日付の部分が分かるようにしたかったんだけど、無理っぽい。

よし、これでやりたいことは一通り出来た。

【ラズパイ】スイッチをモジュール化する

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

前回作成したサンプルソースコードを元に、スイッチ処理をモジュール化します。

やり方としては、スイッチの押下検出をスレッドで動かしてメインスレッドで取り出す、という方式が良いと思います。

GPIOのエッジ検出で割り込み処理する、という方法もありますが、このスイッチモジュールの仕様上、それは難しそうです。

import RPi.GPIO as GPIO
import time
import threading

X_p = 0
Y_p = 0
Z_p = 0
A_p = 0
B_p = 0
C_p = 0
D_p = 0

Non = ""
Key = Non

thread = None
Loop = True

def PinsInit(x, y, z, a, b, c, d):
 省略

def Start():
    thread = threading.Thread(target=threadProcess)
    thread.start()

def Stop():
    global Loop
    Loop = False

def GetKey():
    global Key
    ret = Key
    Key = Non
    return ret

def threadProcess():
    global Key

    col = 1
    swState = [0] * 12
    while Loop:
        if col == 1:
            GPIO.output(X_p, GPIO.LOW)
            GPIO.output(Y_p, GPIO.HIGH)
            GPIO.output(Z_p, GPIO.HIGH)
        elif col == 2:
            GPIO.output(X_p, GPIO.HIGH)
            GPIO.output(Y_p, GPIO.LOW)
            GPIO.output(Z_p, GPIO.HIGH)
        else:
            GPIO.output(X_p, GPIO.HIGH)
            GPIO.output(Y_p, GPIO.HIGH)
            GPIO.output(Z_p, GPIO.LOW)

        # 1押下
        if swState[1] == 0 and col == 1 and GPIO.input(D_p) == 0:
            # チャタリング回避
            time.sleep(0.05)
            if GPIO.input(D_p) == 0:
                Key = "1"
                swState[1] = 1
        # 1押下戻し
        elif swState[1] == 1 and col == 1 and GPIO.input(D_p) == 1:
            Key = Non
            swState[1] = 0
        # 2押下
        if swState[2] == 0 and col == 2 and GPIO.input(D_p) == 0:
            # チャタリング回避
            time.sleep(0.05)
            if GPIO.input(D_p) == 0:
                Key = "2"
                swState[2] = 1
        # 2押下戻し
        elif swState[2] == 1 and col == 2 and GPIO.input(D_p) == 1:
            Key = Non
            swState[2] = 0
        # 3押下
        if swState[3] == 0 and col == 3 and GPIO.input(D_p) == 0:
            # チャタリング回避
            time.sleep(0.05)
            if GPIO.input(D_p) == 0:
                Key = "3"
                swState[3] = 1
        # 3押下戻し
        elif swState[3] == 1 and col == 3 and GPIO.input(D_p) == 1:
            Key = Non
            swState[3] = 0
        # 4押下
        if swState[4] == 0 and col == 1 and GPIO.input(C_p) == 0:
            # チャタリング回避
            time.sleep(0.05)
            if GPIO.input(C_p) == 0:
                Key = "4"
                swState[4] = 1
        # 4押下戻し
        elif swState[4] == 1 and col == 1 and GPIO.input(C_p) == 1:
            Key = Non
            swState[4] = 0
        # 5押下
        if swState[5] == 0 and col == 2 and GPIO.input(C_p) == 0:
            # チャタリング回避
            time.sleep(0.05)
            if GPIO.input(C_p) == 0:
                Key = "5"
                swState[5] = 1
        # 5押下戻し
        elif swState[5] == 1 and col == 2 and GPIO.input(C_p) == 1:
            Key = Non
            swState[5] = 0
        # 6押下
        if swState[6] == 0 and col == 3 and GPIO.input(C_p) == 0:
            # チャタリング回避
            time.sleep(0.05)
            if GPIO.input(C_p) == 0:
                Key = "6"
                swState[6] = 1
        # 6押下戻し
        elif swState[6] == 1 and col == 3 and GPIO.input(C_p) == 1:
            Key = Non
            swState[6] = 0
        # 7押下
        if swState[7] == 0 and col == 1 and GPIO.input(B_p) == 0:
            # チャタリング回避
            time.sleep(0.05)
            if GPIO.input(B_p) == 0:
                Key = "7"
                swState[7] = 1
        # 7押下戻し
        elif swState[7] == 1 and col == 1 and GPIO.input(B_p) == 1:
            Key = Non
            swState[7] = 0
        # 8押下
        if swState[8] == 0 and col == 2 and GPIO.input(B_p) == 0:
            # チャタリング回避
            time.sleep(0.05)
            if GPIO.input(B_p) == 0:
                Key = "8"
                swState[8] = 1
        # 8押下戻し
        elif swState[8] == 1 and col == 2 and GPIO.input(B_p) == 1:
            Key = Non
            swState[8] = 0
        # 9押下
        if swState[9] == 0 and col == 3 and GPIO.input(B_p) == 0:
            # チャタリング回避
            time.sleep(0.05)
            if GPIO.input(B_p) == 0:
                Key = "9"
                swState[9] = 1
        # 9押下戻し
        elif swState[9] == 1 and col == 3 and GPIO.input(B_p) == 1:
            Key = Non
            swState[9] = 0
        # *押下
        if swState[10] == 0 and col == 1 and GPIO.input(A_p) == 0:
            # チャタリング回避
            time.sleep(0.05)
            if GPIO.input(A_p) == 0:
                Key = "*"
                swState[10] = 1
        # *押下戻し
        elif swState[10] == 1 and col == 1 and GPIO.input(A_p) == 1:
            Key = Non
            swState[10] = 0
        # 0押下
        if swState[0] == 0 and col == 2 and GPIO.input(A_p) == 0:
            # チャタリング回避
            time.sleep(0.05)
            if GPIO.input(A_p) == 0:
                Key = "0"
                swState[0] = 1
        # 0押下戻し
        elif swState[0] == 1 and col == 2 and GPIO.input(A_p) == 1:
            Key = Non
            swState[0] = 0
        # #押下
        if swState[11] == 0 and col == 3 and GPIO.input(A_p) == 0:
            # チャタリング回避
            time.sleep(0.05)
            if GPIO.input(A_p) == 0:
                Key = "#"
                swState[11] = 1
        # #押下戻し
        elif swState[11] == 1 and col == 3 and GPIO.input(A_p) == 1:
            Key = Non
            swState[11] = 0

        col += 1
        if col > 3:
            col = 1
        
        time.sleep(0.005)

Start()でthreadProcess()をバックグラウンドで実行、Stop()でLoop=Falseになるまで実行します。

スイッチを押すと、その値がKeyに入るので、それをGetKey()で取り出します。

変数でワンクッション置いているのは、ボタン押しっぱなしで複数回キー入力を検出するのを防ぐためです。

def __main__():
    GPIO.setmode(GPIO.BCM)

    SW.PinsInit(21, 22, 23, 24, 25, 26, 27)
    SW.Start()

    try:
        while True:
            key = SW.GetKey()
            if key != "":
                print(key)

            time.sleep(0.1)
    except KeyboardInterrupt:
        SW.Stop()
        GPIO.cleanup()

__main__()

ここまで出来れば、他モジュールと組み合わせることも出来そうです。

【北海道大戦】対戦ルールを検討する

さて、前回までで、マップ関連は完成しました。

次回からバトル関連を作成していこうと思うのですが、バトルのルールを決めておく必要があります。

まずは、コンピューターだけでバトルさせて、どの市町村が勝利するかを見てみたいと思います。

ということで以下の様にしていました。

  • 各市町村、1ターンに1回行動できる
  • 行動順はランダムで並び替えされる
  • 1ターンにつき、隣接する市町村に攻撃を仕掛けることが出来る
  • 戦力値は人口×0.5~3.0(乱数)で決定される
  • 戦力値が大きい方が勝利する
  • 攻撃側が勝利した場合は防御側の市町村を吸収合併する。
  • 防御側が勝利しても何も無し。
  • 最終的に市町村が残り1つになるまで繰り返す。

こんな感じでどうだろうか?

まずは、作ってみて動かしてみましょうか。

【北海道大戦】離島問題を解決する

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

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

https://github.com/takishita2nd/HokkaidoWar

さて、前回で隣接都市とのリンクを作成しましたが、

1つ問題がありまして、

それは離島とのリンクです。

今のままではマスの隣接関係でリンクを確認しているので、マスが離れている離島との隣接関係がありません。

具体的には、

  • 奥尻ー江差
  • 礼文ー稚内
  • 礼文ー利尻
  • 礼文ー利尻富士
  • 利尻富士ー稚内

この隣接関係を作成しなければ行けません。

※ちなみに、天売島、焼尻島は羽幌に属しています。

今回はUp(),Down(),Left(),Right()処理に例外を入れることで対応します。

マップそのものが変わってしまうと、ここの処理も修正しなければ鳴りませんが、他に有効な手段が思いつかないので、仕方がありません。

    class Map
    {
        public Map Up { 
            get {
                var field = Singleton.GetFieldMap();
                if(_x == 18 && _y == 0)
                {
                    return field.GetMap(23, 0);
                }
                else if(_x == 23 && _y == 0)
                {
                    return field.GetMap(18, 0);
                }
                else if (_x == 20 && _y == 1)
                {
                    return field.GetMap(18, 0);
                }
                else
                {
                    return field.GetMap(_x, _y - 1);
                }
            }
        }

        public Map Down
        {
            get
            {
                var field = Singleton.GetFieldMap();
                if(_x == 18 && _y == 0)
                {
                    return field.GetMap(19, 1);
                }
                else
                {
                    return field.GetMap(_x, _y + 1);
                }
            }
        }

        public Map Left
        {
            get
            {
                var field = Singleton.GetFieldMap();
                if (_x == 2 && _y == 29)
                {
                    return field.GetMap(0, 29);
                }
                else if(_x == 19 && _y == 1)
                {
                    return field.GetMap(18, 0);
                }
                else if (_x == 23 && _y == 0)
                {
                    return field.GetMap(20, 1);
                }
                else
                {
                    return field.GetMap(_x - 1, _y);
                }
            }
        }

        public Map Right
        {
            get
            {
                var field = Singleton.GetFieldMap();
                if (_x == 0 && _y == 29)
                {
                    return field.GetMap(2, 29);
                }
                else if(_x == 18 && _y == 0)
                {
                    return field.GetMap(20, 1);
                }
                else if (_x == 20 && _y == 1)
                {
                    return field.GetMap(23, 0);
                }
                else
                {
                    return field.GetMap(_x + 1, _y);
                }
            }
        }

こんな感じで離島とのリンク関係を強引に作りました。