2. 基本的なテクニックを学ぶ¶
前節で作成したモデルを編集しつつ、モデル作成のための様々な基本テクニックを学んでいきます。
2.1. エージェントの方向を変える¶
まず、エージェントの進む方向を変えてみます。下記のように、agt_initでdirection変数に45を代入して実行してみましょう。
def agt_init(self):
self.x = 25
self.y = 25
self.direction = 45 # この行を追記
なお、コードの行内で#
以降の文字は無視されます。これをコメントアウトといいます。コード内にメモを残すのに便利です。
実行すると、鳥エージェントが右上に飛んでいきます。
エージェントの向きはdirection変数によって決まり、その値は下図のように度数法で設定します。初期値は0だったため右方向に動きましたが、45を代入したため右上に飛んだわけです。
次に、エージェントの進む方向をランダムにしてみましょう。下のように書きます。
def agt_init(self):
self.x = 25
self.y = 25
self.direction = rand() * 360 # この行を編集
ここで、randは0~1のランダムな値(一様乱数)を発生させる関数です。rand() * 360
と書くことで、0~360のランダムな値を意味します。*
は掛け算の記号です。
実行・停止を繰り返すと、鳥エージェントの飛ぶ向きが毎回変わることが分かります。
2.2. 関数¶
ここで、関数について改めて説明しておきます。関数とは以前に説明した通り処理をひとまとめにしたもので、以下の3つの要素からなります。
引数 - 関数へ渡す値
処理 - 引数をもとに関数が行う動作
返り値(戻り値) - 処理をもとに関数が返す値
ここで、引数は複数ある場合もあれば、存在しない場合もあります。また返り値がなかったり、あっても使わない場合もあります。
たとえばforward関数の場合、引数と処理と返り値は以下のようになります。
引数 - 進む距離
処理 - 引数の値だけ前に進む
返り値(戻り値) - 正常終了時は-1(使わなくてもかまわない)
self.forward(10) # 引数の数10だけ前に進む
あるいはrand関数の場合は、以下のようになります。
引数 - なし
処理 - 0~1の一様乱数を生成する
返り値(戻り値) - 処理で生成した一様乱数
r = rand() # rand関数の返り値(0~1の一様乱数)を変数rに代入する
2.3. エージェントを一度に複数生成する¶
今度は、エージェントを一度にたくさん生成してみましょう。モデルツリーのUniverseをクリックしてUniverseのルールエディタに移動し、下のようにcreate_agtを編集します。
def univ_init(self):
create_agt(Universe.oozora.tori, num=100) # 「, num=100」を追記
num=100
と記述することで、エージェントを100体生成することができます。
この状態で実行してみましょう。それぞれのエージェントの向きをランダムに与えたため、100体の鳥エージェントが画面中央から円形に散らばって飛んでいきます。
2.4. エージェントとエージェント種別¶
ここで、「エージェント」と「エージェント種別」について説明しておきましょう。ツリー上のtoriやcreate_agtのカッコ内のUniverse.oozora.toriは、は「エージェント」そのものではなく「エージェント種別」とです。これはエージェントを生成するためのひな形のようなものと考えてください。create_agtは、指定したエージェント種別から具体的なエージェントを作るという操作を意味します。
共通のエージェント種別から作られたエージェントは共通の行動ルールと変数を持ちます。行動ルールはルールエディタで、変数はモデルツリーで規定されます。ただし、変数の値はそれぞれのエージェントで異なります。さきほど作成した100体の鳥エージェントがそれぞれ異なる向きに飛んだのは、「direction変数にランダムな値を代入する」という共通のルールにより、それぞれのエージェントのdirection変数に異なる値が代入されたためです。
2.5. エージェントの速度を変える¶
今後はエージェントの速度を変えてみます。これを行うためには、エージェントに速度を表すための新たな変数を追加する必要があります。モデルツリーを下図のように操作して、toriエージェント(エージェント種別)にspeedという変数を追加してください。
次いで、speedの値を設定します。下のように、agt_initでspeedに1 + rand()
を代入してください。rand()
は0~1の間のランダムな値ですから、1 + rand()
で1~2の間のランダムな値を意味します。
def agt_init(self):
self.x = 25
self.y = 25
self.direction = rand() * 360
self.speed = 1 + rand() # この行を追記
最後に、鳥の速度をspeed変数の値にします。下のように、forwardのカッコの中をspeed変数にしてください。
def agt_step(self):
self.forward(self.speed) # 1ステップにspeed変数の値だけ進む
この状態で実行しましょう。個々のエージェントに異なる速度が与えられたため、きれいな円とはならないことが分かります。このように、エージェントに自由に変数を追加し、それをモデルに反映させることができます。
実行したら、speed変数を1に戻しておいてください。
def agt_init(self):
self.x = 25
self.y = 25
self.direction = rand() * 360
self.speed = 1 # この行を編集
2.6. エージェントを毎ステップ生成する¶
今後は、エージェントを毎ステップ生成してみましょう。Universeのルールエディタのuniv_step_beginにcreate_agtを記述することで、エージェントを各ステップの最初に生成することができます。ツリーのUniverseをクリックしてルールエディタを開き、以下のように書き直してください。
def univ_init(self):
pass
# create_agt(Universe.oozora.tori, num=100)
def univ_step_begin(self):
create_agt(Universe.oozora.tori, num=100)
ここで、univ_initに書いてあったコードはコメントアウトとpassを用いて無効化しています。これについてはこの後で説明します。
この状態で実行しましょう。エージェントが毎ステップ生成されるため、円が何層にもなることが分かります。
2.7. コメントアウトとpass¶
先ほど、univ_initの中身を無効化するためにコメントアウトとpassを用いました。これについて補足しておきます。
コメントアウトは以前にも登場しましたが、行の#
以降が無効化される機能です。ここではcreate_agtの行頭に#
を記述することで、行をまるごと無効化しています。このように、コメントアウトはコードを一時的に変更するときにも便利です。コメントアウトとその解除は、対象の行にカーソルを合わせて「ctrl+/」で行うことができます。複数行を選択して「ctrl+/」で、複数行のコメントアウトとその解除を一気に行うこともできます。
また、ここでuniv_initにpassを追記しています。passは「何もしない」ということを表すルールです。実はここでこれを書かないとエラーになってしまいます。ルールはインデント内に記述するということを思い出してください。逆にいえば、インデント内に何かルールを書かないと「インデントがない」というエラーになってしまいます。そこで、「何もしない」というルールを明示するためにpassを記述する必要があります。
2.8. ステップの流れ¶
ここで、ステップの流れについて説明しておきましょう。ステップとはartisoc Cloudの時刻単位です。ステップのはじめにuniv_step_beginが実行され、その後は各エージェントのagt_stepが順に実行されます。ステップの最後にuniv_step_endが実行されます。シミュレーション実行中はこのサイクルを延々と繰り返すことになります。
シミュレーション開始時にはuniv_initが、終了時にはuniv_finishがそれぞれ1度だけ実行されます。また、create_agt関数によりエージェントが生成されたときはその都度agt_initが実行されます。
2.9. 条件分岐文¶
引き続きルールを編集しつつ、「条件分岐文」という重要な手法について学びます。
「10ステップ目までエージェントを毎ステップ生成する」というモデルを作成したいとします。これを行うには、現在エージェントを生成している部分に、「現在のステップ数が10以下の場合のみ、エージェントを生成する」という条件付きのルールを記述する必要があります。このような条件付きのルールを表す文を条件分岐文(もしくはif文)といい、以下のように記述します。
if [条件文]:
[条件文が真のときに行う処理]
else:
[条件文が偽のときに行う処理]
ここで、処理はインデントを下げて記述します。また、else以下は省略することができます。
ifの直後に来る条件文とは、数学でいう命題と同じで、真偽のどちらかが決まる文を指します。たとえば以下のようなものです。
1 < 5 # [1は5未満である] ⇒ 真(True)
4 >= 8 # [4は8以上である] ⇒ 偽(False)
x == 4 # [xと4は等しい] ⇒ xが4のとき真(True)、それ以外のとき偽(False)
「現在のステップ数が10以下の場合のみ、エージェントを生成する」というルールは、univ_step_beginを編集して以下のようにします。
def univ_step_begin(self):
if count_step() <= 10:
create_agt(Universe.oozora.tori, num=100)
count_stepは現在のステップ数を取得する関数です。count_step() <= 10
で、「現在のステップ数が10以下である」という条件文を表します。処理を行う行(crate_agtの行)はifの行からインデントを下げることに注意してください。11ステップ目以降は何もしないですので、今回はelse以下を記述する必要はありません。
この状態で実行してみると、以下のようになります。10ステップ目までエージェントを生成し、それ以降は生成しないので、発生したエージェントがドーナツ型に広がっていきます。
応用編として、「毎ステップ、20%の確率でエージェントを生成する」というルールを書いてみましょう。これを実現するには、一様乱数randを用いて以下のように書きます。
def univ_step_begin(self):
if rand() < 0.2:
create_agt(Universe.oozora.tori, num=100)
randは0~1の一様乱数ですので、0.2未満になる確率は20%です。したがって、20%の確率でif文の条件文が真になり、create_agtが実行されることになります。
これを実行すると下図のようになり、およそ5ステップに1回エージェントが生成されていることが分かります。
一般に、確率rで実行する処理は以下のように書くことができます。
if rand() < r:
[確率rで実行する処理]
2.10. 繰り返し文¶
続いて、「繰り返し文」と呼ばれる手法を練習しましょう。
再び、「シミュレーション開始時に100エージェントを生成する」というルールを考えます。先ほどはcreate_agt関数に「num=100」と書くことで100エージェントをまとめて生成しました。今度は「エージェントを1体生成する操作を100回繰り返す」というルールで書いてみます。
このように、同じ操作を繰り返すルールを表す文を繰り返し文(もしくはfor文)といい、一般的には以下のように書きます。
for [変数] in range([繰り返す回数]):
[繰り返す処理]
処理はインデント内に書くことに注意してください。forの直後に来る変数は何でもかまいませんが、慣習としてはiをよく使います。(理由は後で説明します)
シミュレーション開始時にエージェントを1体生成する処理を100回繰り返すには、univ_initに以下のように書きます。forの次の行はインデントを1段下げることに注意してください。univ_step_beginに書いた内容は削除するか、コメントアウトしておいてください。
def univ_init(self):
for i in range(100):
create_agt(Universe.oozora.tori)
def univ_step_begin(self):
pass
実行すると、確かにシミュレーション開始時にエージェントが100体生成されたことが分かります。univ_init内で100回の繰り返し処理を行ったためです。
続いて、for文をもう少し高度に使う練習をします。「エージェントを100体生成し、それぞれに0度~99度の向きを与える」というルールを考えてみましょう。このルールを実現するためには、このように書きます。
def univ_init(self):
for i in range(100): # インデント以下の処理を、iを0から99に変えながら100回繰り返す
one = create_agt(Universe.oozora.tori) # 生成したエージェントを変数oneに代入
one.direction = i # エージェントのdirectionにiを代入
新しい事項がいくつか出てきたので、1つずつ説明します。まず、for i in range(100):
は、実はインデント内の処理を単に100回繰り返すのではなく、iを0から99まで変化させながら繰り返すことを意味します。1ではなく0から数えるのは、プログラミングのお約束のようなものです。変数名にiをよく使うのは、iがinteger(整数)の頭文字だからです。
# 変数を0~n-1まで変化させながらn回繰り返す処理
for [変数] in range(n):
[変数を用いた処理]
繰り返し処理の中に入ります。one = create_agt(Universe.oozora.tori)
と書くことで、生成したエージェントを新たな変数oneに代入することができます。実はcreate_agt関数の返り値がエージェントそのものなのです。つまり、ここで変数oneは生成したエージェントそのものを表します。このように、変数は数値だけでなくエージェントそのものを値として持つこともできます。この点は後で補足します。
次の行でone.direction = i
と書くことで、生成したエージェントoneのdirection変数に数値iを代入することができます。このように「.」を持ちいることで、エージェントの持つ変数にアクセスできます。自分自身を表す「self」は既に何度も登場していますが、これがoneに置き換わっただけです。
※なお、エージェントのdirectionはagt_initでランダムに設定したはずではないかと疑問に思われるかもしれません。agt_initはcreate_agtした瞬間に実行されますので、one = create_agt(Universe.oozora.tori)
の行ではたしかにdirectionはランダムに設定されています。しかし、その次の行でone.direction = i
とdirection変数にiを代入しています。つまりランダムに設定されていたdirectionをiで上書きしているわけです。
この処理を、iを0から99まで変化させながら繰り返すことになります。その結果として、directionが0から99までの計100体のエージェントが生成されるわけです。
このモデルを実行すると、下図のように鳥エージェントが扇形に飛んでいきます。
2.11. 変数とデータ型¶
ここで、変数とデータ型について説明しておきます。以前にも説明しましたが、変数とは値を保存しておくための箱のようなものです。そしてデータ型とは、変数が持つ値の種類のことです。
主なデータ型には、整数型や実数型など数値を表す型があります。たとえばエージェントのx座標、y座標、directionなどは数値型のデータを値として持つ変数です。
数値だけでなく、他にも様々なデータ型があります。たとえばさきほど登場したエージェント型はエージェントそのものを表すデータ型で、create_agt関数の返り値の型でもあります。one = create_agt()
と書くことで、変数oneに生成したエージェントをエージェント型の値として代入することができます。他にも、文字列を表す文字列型、真偽値(TrueまたはFalse)を値として持つ論理型(もしくはブール型)などがあります。
他には、個々のデータでなくデータの集まりを扱う型として集合型(セット型)やリスト型、辞書型などもあります。
それぞれのデータ型については登場したときに解説しますが、このように、変数は数値に限らず様々な種類のデータを扱うことができることを覚えておきましょう。
self.x = 3 # エージェントの持つ変数xに整数型の値3を代入
one = create_agt(Universe.oozora.tori) # 変数oneに生成したtoriエージェント(エージェント型)を代入
one.direction = rand() * 360 # エージェントの持つ変数directionに実数型の値rand() * 360を代入
s = “artisoc Cloud” # 変数sに文字列型の値”artisoc Cloud”を代入(文字列を「”」で挟むことで文字列型の値を生成できる)
2.12. コントロールパネルで変数の値を設定する¶
コントロールパネルとは、変数の値などをGUI上で操作できるようにする機能です。いちいちルールを書き換えず、パラメータの値を手軽に変更してシミュレーションを実行することができます。ここでは、下図のように鳥の数をスライドバー操作で設定できるようにしてみましょう。
まずは鳥の数を表す変数を作成します。下図のように、ツリーを操作してUniverseの直下に変数「hasuu」を追加します。
次に、今作った変数をルールエディタ上で鳥の数に設定します。現在のルールでは、univ_init内のfor文の繰り返し回数が鳥の数を意味したので、下のようにrangeのカッコ内を書き換えます。Universeの直下の変数はUniverse.[変数名]
でアクセスできます。
def univ_init(self):
for i in range(Universe.hasuu): # カッコ内を変数hasuuに変更
one = create_agt(Universe.oozora.tori)
one.direction = i
最後に、変数hasuuをスライドバーで操作できるようにします。出力画面に移動し、画面左上の「コントロールパネル」からコントロールパネル設定のページを開き、種類としてスライドバーを選択します。
残りの設定を下図のように指定してOKを押下すると、画面上にスライドバーが作成されます。これで完成です。
実行してみると、スライドバーの値によって発生する鳥の数が変わることが分かります。このように、画面上の操作で簡単にシミュレーションの設定を変更することができます。
2.13. 変数の種類¶
artisoc Cloudの変数は、作成する場所によっても分類されます。
Universe変数はツリー上でUniverseの直下に作成する変数で、モデル全体の性質を表します。例えば先ほど作成したUniverse.hasuuがそれです。鳥の数はモデル全体の性質を表す変数ですので、Universe変数として作成したわけです。
エージェント変数はツリー上でエージェントの直下に作成する変数で、エージェントの性質を表します。例えば鳥の位置(x, y)、向き(direction)、速度(speed)などがそれです。speedをエージェント変数として作成したのは、それがモデル全体でなく個々のエージェントの性質を表すものだからです。
ローカル変数はルールエディタ上で一時的に使用する変数です。次のステップまで変数の値を保存しておく必要がない場合に用います。例えば繰り返し文で登場した鳥エージェントoneやfor文の変数iがそれです。これらの値はfor文が繰り返すたびに更新されますので、いわば使い捨ての変数と言えます。
まだ登場していませんが空間変数というツリー上で空間の直下に作成する変数もあり、これは空間の性質を表します。
このように、変数を作成する際にはその役割に応じて作成する場所を考える必要があります。