【Before We Leave】チュートリアルを攻略

戦闘の無い優しい世界で都市開発を行うゲームです。

EPIC Gamesでダウンロードできます。

週間アスキーにて紹介されていたのでプレイして見ました。

ただ、一つ問題がありまして、

日本語化されていないんですよね。

なので、チュートリアルを解説付きでプレイしていきます。

左上に指示が出ていますので、この通りに進めていきます。

マウスの右クリック・ドラッグでカメラを移動、ホイールの回転でズームを行います。

道(Road)を3つ、小屋(HUT)を3つ作成します。

左下に矢印が出ているので、どれが該当するアイコンかが分かると思います。

ジャガイモ農場(Potato Field)2つと井戸(well)を作成します。

農場系は井戸の周りに作成すると効率が上がります。

逆に工業系の近くに置くと、汚染が発生するので、効率は下がります。

木切り場(woodcutter)を森の近くに配置します。

これで木材の供給が可能になります。

森を3つ破壊し、石を3つ獲得します。

矢印の施設でツールを作成します。

施設をクリックすると、右下にウィンドウが出るので、緑のボタンを押すと、木材からツールを5つ獲得します。

今のところツールを作成するのはこの方法しかありません。

冒険者の小屋(Explorers Hut)と図書館(Library)を建築します。

冒険者の小屋は地図上のエンシャントテクノロジー(緑色のカプセルみたいなの)を回収し、グリーンテクノロジーに変換します。

図書館はこのグリーンテクノロジーを消費してテクノロジーを獲得します。

図書館でエレベータのテクノロジーを獲得し、エレベーターを作成します。

図書館をクリックすると右下にウィンドウが表示されるので、緑のボタンを押すと、

この画面が出るので、エレベーターをリサーチします。

エレベーターは高台に上るために必要になります。

高台と平地の間に設置します。

そのまえに、高台まで道が繋がっている必要があります。

図書館でマイニングを獲得します。

採石場(Quarry)を建設します。矢印があるmountainの隣に設置します。

修復(Repair)を獲得します。

Hutの数を増やして、人間の数を25以上にします。

倉庫(Warehouse)をリサーチして倉庫を建設します。

倉庫を建設するには2つスペースが必要になります。

また、この頃にはツールが足りなくなると思うので、ツールを生産する必要があります。

次に倉庫に溜める資材を選択します。次のチュートリアルに進むためには最低3つ選択する必要があります。

次はMetalworkをリサーチします。

そうすると、鉄鉱山(Iron mine)を建築できるようになるので、Rusty Hulkの側に建設します。

次は発電所を修復します。

発電所を修復すると、パワー(power)を獲得できます。

パワーは発電所のみで獲得できます。

それが出来たら鉄精錬所(Iron Smelter)とToolsmithを建設します。

これで鉄鉱石とパワーを消費し、鉄とツールの供給が可能になります。

この2つは汚染を発生させるので、農場とは離れたところに設置するといいでしょう。

次はガーデニング(Gardening)をリサーチし、野菜畑2つとハーブ畑を2つ建設します。

これも井戸の近くに設置すると効率が上がります。

最後に船を修復します。

船を修復したら船を操作して新大陸を探します。

新大陸を発見したら、船を大陸の側に移動させて右下の緑のボタンから上陸させます。

これで序盤はクリア。

まだまだ続きますが、新大陸に上陸した後も何をすれば良いかが分かるはずです。

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

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

最新ソースはこちら(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

【ダイエット】実践していることその3、ダイエットのためにお金をケチらない

その1についてはこちら。

【ダイエット】実践していることその1、体重計を買うべし

その2についてはこちら。

多少波がありますが、今、体重が減少傾向にあります。

今日明日頑張ればもっと体重落ちそう。

ダイエットを本気で取り組もうと思っているならば、あえて、高いコストをかけるというのも1つの手段ダと思います。

自分の場合は、トレーニングウェアに1万円のスポーツブランド(デサント)を購入しましたし、

トラッカーには2万円のFitbit Versa 2を購入。

体重測定には1万円のFitbit Aria2を使用していますし、

ランニングでは2.6万円のソニー WF-SP800Nを購入しました。

SAVASのデカイ袋のプロテインをほぼ毎日飲んでいます。

なぜ高い物を選択するのかというと、

ダイエット・トレーニングの効率を高めるという意味もありますが、

ダイエットを諦めるハードルを下げないためでもあります。

例えば、こんな経験は無いですか?

近所に安くていつでも使えるトレーニングジムが出来たと聞いて、会員になったものの、結局月に1回しか通ってない。

いつでも通えると思ったら明日でいいやと思ったり、結局めんどくさいと思ったり。

そんな状態のまま数ヶ月、無駄にジムの月額使用料を支払っている。

(まさしく過去の自分です。)

後々考えれば、こんなことにお金を使わずに、お金はかかるけど完全予約制の専属トレーナー付きのジムに通った方が、遙かに効率的だと思いませんか?

完全予約制ですから必ずジムに通わなければなりませんし、トレーナーがきちんとトレーニングを指導してくれます。

ここまでやったら成果が出ないと、もったいないじゃ無いですか。

これは一つの例でしたが、

結局、安いコストしかかけていないと、止めようと思ったら簡単に止めることが出来てしまいます。

しかし、高いコストをかけることで、「ここまでお金をかけたんだから成果を出さないともったいない。」という考えが出てくると思うんです。

つまり、ダイエットを諦めるハードルが全然違ってくるんです。

ハードルを高くすることは継続に繋がります。

本気でダイエットをしたいと考えているのならば、安物で揃えてそれで成果が出ればラッキー、という考えでは無くて、高い金をかけてるんだから絶対成果を出してやる!という考えじゃないと無理だと思います。

まさしくそれがダイエットに対する本気度だと思いますよ。

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

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

最新ソースはこちら(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

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

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