はじめに
マインクラフトEducation Edition(マイクラEE)では、MakeCodeというビジュアルプログラミングツールを用いて、マイクラの世界の中でプログラムを組むことができます。
MakeCodeはGoogle Blocklyをベースに作られていますが、小学生・中学生を対象にNSSOLが提供しているK3Tunnelもまた、Google Blockyをベースとしています。いうなれば、MakeCodeとK3Tunnelは兄弟のような関係性なのです。今回は、K3Tunnelのサイト上で公開されているプログラムを、MakeCodeに移植してマインクラフトの世界で動かしてみようと思います。
プログラムの移植
K3Tunnelではビジュアルプログラムのサンプルが公開されています。今回は、比較的マイクラの世界で可視化しやすいプログラムとして、ネズミ算を採用しようと思います。
K3Tunnelのネズミ算
K3Tunnelのネズミ算は、以下のような問題設定となっています。
親も子も孫もひ孫も月々に12匹(オス6匹、メス6匹)ずつ産む時、12ヶ月でどれくらいになるか。
MakeCodeへの変換
前節で紹介したネズミ算のプログラムをMakeCodeに変換します。MakeCodeとK3Tunnelとでは、文字列を扱うブロックや画面出力用のブロックに多少の差異はありますが、基本的にはそのまま移植ができそうです。K3Tunnelのネズミ算を単純にMakeCodeに移植すると以下のようになります。
このままではマイクラのチャットに計算結果を表示するだけなので、もう少しマイクラらしくしてみます。K3Tunnelでは、グラフによってネズミ算式に増える傾向を可視化していますが、今回はMOBを増やす形でネズミ算の結果を可視化します。増やすMOBは何でもよいのですが、せっかくマイクラを舞台にしているので、クリーパーに登場していただきましょう。
MOBをネズミ算式に増やす
初期値としてのつがいの?クリーパーは、単純にモンスターをスポーンさせるブロックで実現できそうです。
では、ネズミ算式に増える部分の実装はどうなるでしょう。スポーンでは一度に1体のクリーパーしか生み出さないため、スポーンを繰り返すという実装にした場合、スポーン数が多くなると非常に多くに処理時間を要してしまいます。そこで、コマンドブロックを使って、同時に複数のMOBをスポーンさせるコマンドを実行します。
/execute as @e[<エンティティの指定>] run /summon creeper <スポーン地点>
\@e[<エンティティの指定>]
で指定される対象が、run
の後に記述されているコマンド(今回は/summon)を実行するというコマンドです。対象が複数となる指定条件を入れると、指定された対象の数だけコマンドが同時実行され、複数のMOBを<スポーン地点>に出現させることができます。
では、どのようにエンティティを指定するとよいでしょう。単純に数を指定したい場合、c=N
という表記法を使うことができます(Nは指定したい数)。c=5
でクリーパーをスポーンさせた場合が以下の動画です。
ただ、この構文を今回のケースで使おうとすると一つ問題があります。Nとして指定したい数が、月ごとに異なるのです。変数が使えると便利なのですが、この構文の中に組み込めるような変数は、今のところマイクラには実装されていないようです。月ごとにコマンドブロックを分けて、スポーン数をハードコードしたり、functionを使ったりするのは、MakeCodeの外側に課題に関するプログラム要素を持ち込むという意味でちょっと反則技っぽいので、なんとかMakeCode側からの制御だけでネズミ算を実行したいところです。
エンティティの指定としては、指定した範囲内に存在するMOBを対象とすることも可能です。例えば
[x=0,y=0,z=0,dx=10,dy=20,dz=30]
とすると、x=0,y=0,z=0の地点とx=10,y=20,z=30の地点を対角とする直方体のエリア内に存在するエンティティを対象として、存在するエンティティ数分のコマンドを実行することになります。クリーパーが5匹いる柵で囲われた左側のエリアを対象として、右側の柵にスライムをスポーンさせるコマンドを実行した結果が次の動画です。指定したエリア内にいるクリーパーの数だけスライムが生まれています。この特徴を活用し、特定エリア内に存在するエンティティの数をうまくコントロールできれば、期待する並列度でコマンドを実行することができます。
エリア内のエンティティ数の制御
まず、繁殖対象のクリーパーをもれなく数え上げられるように、ネズミ算式に増えるクリーパーが動けるエリアを柵で囲って制限しておきます。このエリアを仮に繁殖エリアと呼ぶことにします。
今回のねずみ算の場合、繁殖エリア内で「翌月いるクリーパーの数」を決めている要素は、「今月いるクリーパーの数」と「一匹のクリーパーから生まれるクリーパーの数」です。そこで、繁殖エリア内のクリーパーを増やすにあたり、「今月いるクリーパーの数」を記憶しておくためのエリアを用意します。このエリアは仮に記憶エリアと呼ぶことにします。繁殖エリア内にいるクリーパーと同数のクリーパーを記憶エリアにスポーンさせておくことで、記憶エリア内にいるクリーパーの数(=今月いるクリーパーの数)をベースに繁殖エリア内のクリーパーを増やすことができるのです。
コマンドブロックとしての手続きは以下の3段階になります。
①記憶エリア内のエンティティを消す(0にする)
②繁殖エリア内のクリーパーと同数のクリーパーを記憶エリアにスポーンさせる
③記憶エリア内にいるクリーパーと総数のクリーパーを繁殖エリアにスポーンさせる
④③を「一匹のクリーパーから生まれるクリーパーの数」分繰り変えす
今回の実装では、繁殖エリアを[x=-15,y=-60,z=40,dx=30,dz=30]
、記憶エリアを[x=-15,y=-60,z=-15,dx=30,dz=30]
と設定しています。
player.onChat("k3tunnel", function () {
月 = 1
つがいの数 = 1
for (let index = 0; index < 1; index++) {
mobs.spawn(mobs.monster(CREEPER), world(0, -60, 60))
}
くりかえし回数 = 2
for (let index = 0; index < くりかえし回数; index++) {
agent.teleport(world(3, -60, 33), SOUTH)
agent.move(FORWARD, 1)
agent.move(BACK, 1)
agent.move(RIGHT, 2)
agent.move(FORWARD, 1)
agent.move(BACK, 1)
agent.move(RIGHT, 2)
for (let index = 0; index < 6; index++) {
agent.move(FORWARD, 1)
agent.move(BACK, 1)
agent.interact(FORWARD)
}
項目名 = "\"生き物の数:\"" + 月 + "ヵ月後"
player.say(項目名)
つがいの数 += つがいの数 * 6
生き物の数 = "" + つがいの数 * 2 + "匹"
player.say(生き物の数)
月 += 1
}
})
各コマンドブロックに配置しているコマンドは以下のようになっています。
①記憶エリア内のエンティティを消す(0にする)
/execute as @e run /kill @e[x=-15,y=-60,z=-15,dx=30,dz=30]
②繁殖エリア内のクリーパーと同数のクリーパーを記憶エリアにスポーンさせる
/execute as @e[x=-15,y=-60,z=40,dx=30,dz=30] run /summon creeper 0 -60 0
③記憶エリア内にいるクリーパーと総数のクリーパーを繁殖エリアにスポーンさせる
/execute as @e[x=-15,y=-60,z=-15,dx=30,dz=30] run /summon creeper 0 -60 0
④の部分はコマンドブロックに感圧板をつないで、必要回数エージェントを往復させることでMakeCodeから制御しています。
プログラムの実行
それでは実際にプログラムを動かしてみます。3か月目までを実行してみた動画です。
3か月目に入ってエージェントの動きが一気に遅くなりました。描画範囲内のクリーパーの数が増えすぎたことが原因と思われます。
先の動画では繁殖エリアのみを撮影していますが、記憶エリアも含めて撮影した動画がこちらです。
各月の計算が完了し、次の月のクリーパーを増やす際に、記憶エリアをリセットして(クリーパーを消して)、繁殖エリアと同数のクリーパーをコピーしていることが見て取れると思います。
4か月目の計算は、10分以上を要してしまったため、動画ではなく画像で結果を記録しました。初期状態からの1か月ごとの変化を並べて表示してみます。
まとめ
今回は、K3Tunnelで公開されているネズミ算のプログラムをベースとして、マイクラEE上でクリーパーをネズミ算式に増やしてみました。時間の経過とともに(クリーパーだけに?)爆発的に増えていく様子が可視化できているのではないでしょうか。
K3Tunnelのサイト上には、他にも面白い課題・プログラムが多数掲載されているので、別のサンプルをマイクラEE上で再現・可視化してみることにも挑戦してみたいです。