はじめに
この記事は、NSSOL Advent Calendar 2025 2日目の投稿です。
この記事のゴール
ロトカ・ヴォルテラ方程式(捕食者と被食者の個体数変動を記述する有名な数理モデル)を、Minecraft Education Edition上で可視化し、ゲームの世界で生態系のダイナミクスを体験できる方法を紹介します。
このモデルは、オオカミとヒツジのような捕食者と被食者の数が時間とともにどのように変化するかを示すもので、数式だけではイメージしづらい「周期的な変動」を視覚的に理解できます。
今回は、当社が提供するK3Tunnelで紹介されているオイラー法による計算方法を参考に、MinecraftのMakeCode(JavaScript)で実装し、ワールド上でオオカミとヒツジの増減を表現します。
ロトカ・ヴォルテラ方程式とは?
ロトカ・ヴォルテラ方程式は、捕食者と被食者の個体数の時間変化を記述する連立微分方程式です。
$$
\begin{aligned}
\frac{dx}{dt} &= a x - b xy \\
\frac{dy}{dt} &= c xy - d y
\end{aligned}
$$
$x$: 被食者(ヒツジ)の数、$y$:捕食者(オオカミ)の数、$t$:時間、$a,b,c,d$は正の実数のパラメータで、それぞれ以下の意味があります。
- $a$:ヒツジの自然増加率
- $b$:捕食によるヒツジの減少率
- $c$:オオカミの自然減少率
- $d$:捕食によるオオカミの増加率
このモデルの面白いところは、捕食者と被食者の数が周期的に変動することです。今回は、この数理モデルをMinecraftの世界で可視化します。
K3Tunnelの紹介と今回のアプローチ
K3Tunnelは、「計算」するための ビジュアル・プログラミング・アプリケーションです。「プログラミングで学ぶ」をコンセプトに、小学生から大人まで学びを楽しむオリジナルコンテンツを提供しています。
今回題材とするロトカ・ヴォルテラ方程式も、K3Tunnel内に、オイラー法を用いて解くためのブロックプログラミングのサンプルがあります。今回はその考え方をベースに、Minecraft Education EditionのMakeCodeで、JavaScriptを使って実装します。
Minecraftへの実装
今回の実装では、Minecraft Education EditionのMakeCode(JavaScript)を使って、ロトカ・ヴォルテラ方程式をオイラー法で数値計算し、その結果をワールド内のMob(オオカミとヒツジ)の数に反映させます。方程式のパラメータはK3Tunnelと同じ値を使いますが、xとyの初期値だけはヒツジ20匹、オオカミ100匹に設定します。K3Tunnelのサンプルではxとyを規格化された実数(捕食者1、被食者0.2)として扱っていますが、Minecraft上で表現する上では群れとしての増減を表現したいので、実際の数を大きくしています。なお、コードの作成においては、実装方針の検討にChatGPT、コードの生成にはClaudeを利用しています。
主なポイント
-
グローバル変数でMob数を管理
sheepCountとwolfCountを用意し、スポーンや削除時に更新します。 -
Mob操作用の関数
-
spawnSheep(count):指定数のヒツジをランダム位置にスポーン -
spawnWolf(count):指定数のオオカミをランダム位置にスポーン -
killSheep(count)/killWolf(count):指定数のMobを削除(名前指定で正確に減らす)
-
-
メイン処理
- ロトカ・ヴォルテラ方程式をオイラー法で計算
- 計算結果とワールド内のMob数の差分を比較し、Spawn/Killで調整
- チャットコマンド
goでシミュレーション開始、resetで環境リセット
以下に、実際のコードを折りたたみ形式で掲載します。
Makecode(Javascript)でのシミュレーション用コード:クリックしてコードを表示
// グローバル変数でヒツジとオオカミの数をカウント
let sheepCount = 0
let wolfCount = 0
// 方程式の定数
let a = 1
let b = 2
let c = 2
let d = 0.8
// 時間間隔と時間発展回数
let dt = 0.01
let times = 2000
/**
* ランダムな位置にヒツジを複数スポーンさせる関数
* @param count スポーンさせるヒツジの数
*/
function spawnSheep(count: number) {
for (let i = 0; i < count; i++) {
// ヒツジカウントをインクリメント
sheepCount++
// ランダムな位置を生成
// X座標: -31 ~ 31
let randomX = randint(-31, 31)
// Y座標: -60で固定
let randomY = -60
// Z座標: -31 ~ 31
let randomZ = randint(-31, 31)
// ヒツジをスポーン
let command = "summon sheep \"sheep"
command += sheepCount
command += "\" ~ ~ ~"
mobs.execute(mobs.target(MY_AGENT),world(randomX,randomY,randomZ),command)
}
//ヒツジがオオカミの攻撃で死なないように強化
player.execute('effect @e[type=sheep] resistance 999999 255 true')
}
/**
* ランダムな位置にオオカミを複数スポーンさせる関数
* @param count スポーンさせるオオカミの数
*/
function spawnWolf(count: number) {
for (let i = 0; i < count; i++) {
// オオカミカウントをインクリメント
wolfCount++
// ランダムな位置を生成
// X座標: -31 ~ 31
let randomX = randint(-31, 31)
// Y座標: -60で固定
let randomY = -60
// Z座標: -31 ~ 31
let randomZ = randint(-31, 31)
// オオカミをスポーン
let command = "summon wolf \"wolf"
command += wolfCount
command += "\" ~ ~ ~"
mobs.execute(mobs.target(MY_AGENT), world(randomX, randomY, randomZ), command)
}
}
/**
* 指定した数のヒツジを減らす関数
* @param count killするヒツジの数
*/
function killSheep(count: number) {
for (let i = 0; i < count; i++) {
let targetname = "sheep" + sheepCount
let command = "kill @e[name=" + targetname + "]"
mobs.execute(mobs.target(MY_AGENT), world(0, 0, 0), command)
sheepCount--
}
}
/**
* 指定した数のオオカミを減らす関数
* @param count killするオオカミの数
*/
function killWolf(count: number){
for (let i = 0; i < count; i++){
let targetname = "wolf" + wolfCount
let command = "kill @e[name=" + targetname + "]"
mobs.execute(mobs.target(MY_AGENT), world(0,0,0), command)
wolfCount--
}
}
/**
* 環境をリセットするチャットコマンド
*/
player.onChat("reset", function () {
sheepCount = 0
wolfCount = 0
mobs.execute(mobs.target(MY_AGENT), world(0, -60, 0), "kill @e[type=!player]")
})
/**
* ロトカ・ヴォルテラ方程式を解き、ヒツジとオオカミを増減させるメイン処理
*/
player.onChat("go", function () {
spawnSheep(100)
spawnWolf(20)
let x = 1
let y = 0.2
let x0 = 0
let y0 = 0
let x1 = 0
let y1 = 0
let x2 = 0
let y2 = 0
for (let i = 0; i < times; i++) {
x0 = x
y0 = y
x1 = a * x0
x2 = b * x0 * y0
x = x0 + dt * (x1 - x2)
y1 = c * x0 * y0
y2 = d * y0
y = y0 + dt * (y1 - y2)
let dx = 100*x - sheepCount
let dy = 100*y - wolfCount
// 時間発展後のヒツジの数が、ワールド内のヒツジの数と1以上乖離している場合はSpawnもしくはKillで頭数調整
if(Math.abs(dx) >= 1){
if(dx > 0){
let spawnCount = Math.floor(dx)
spawnSheep(spawnCount)
}else if(dx < 0){
let killCount = Math.floor(Math.abs(dx))
killSheep(killCount)
}
}
// 時間発展後のオオカミの数が、ワールド内のオオカミの数と1以上乖離している場合はSpawnもしくはKillで頭数調整
if (Math.abs(dy) >= 1) {
if (dy > 0) {
let spawnCount = Math.floor(dy)
spawnWolf(spawnCount)
} else if (dy < 0) {
let killCount = Math.floor(Math.abs(dy))
killWolf(killCount)
}
}
//loops.pause(100)
}
})
シミュレーション結果
時間間隔はdt = 0.01とし、t = 0から20まで2000ステップ計算しました。
各ステップでオオカミとヒツジの数を計算し、ワールド内のMob数との差分が1以上ならSpawnまたはKillでワールド内のMob数を調整しています。
このシミュレーションをMinecraft上で動かし、動画として記録しました。以下の動画では、オオカミとヒツジの個体数が周期的に変化する様子をMinecraft上で確認できます。
グラフによる可視化
さらに、時間発展に応じたオオカミとヒツジの数の推移をMinecraft上でグラフとして表現しました。横軸を時間、縦軸を個体数とし、色付きガラスブロックを使ってヒツジ(青)とオオカミ(赤)のグラフを重ねて表示します。
参考までに、K3Tunnelのブロックプログラミングの計算結果は以下のようになっています。
おわりに
Minecraft Education Editionで数理モデルを表現することで、プログラミング教育やシミュレーションの面白さを体験できます。今回はロトカ・ヴォルテラ方程式をオイラー法で数値計算した結果を、群れの数に反映させる形で可視化を行いました。ロトカ・ヴォルテラ方程式は、被食者数xと捕食者数yが実数である前提となっていますが、実際のい生き物の群れは実数ではなく整数値のみをとる離散系です。そこで、オオカミやヒツジの群れが捕食や出生により1匹単位で確率的に増減するような時間発展モデルでシミュレーションしてみても面白いかもしれません。ChatGPTによると、そういうモデル化もできそうな雰囲気なので、本稿の続編として取り組んでみたいと思います。
Appendix:Minecraft Education Editionへの実装上の注意事項
✅オオカミによるヒツジへの攻撃
- オオカミは近くのヒツジを攻撃してしまうため、耐久力を付与して攻撃を無効化します。
effect @e[type=sheep] resistance 999999 255 true
✅スポーン位置のランダム化
- Mobをスポーンさせる際、座標を乱数で指定して柵内に散らばるようにします。
- 例: randint() を使ってX/Z座標をランダム化。
let randomX = randint(-31, 31)
✅Mob削除の工夫
Minecraft(統合版・教育版)では「指定数だけkill」はできないため、以下の方法を採用:
- スポーン時に通し番号付きの名前を付与(例: sheep1, wolf1)
- 削除時は名前指定でkillし、カウントをデクリメント
- 必要な回数繰り返す数を減らすときの対象の選び方
✅Mobの可視範囲
- プレイヤーから約4チャンク(水平・垂直方向)程度までが視界範囲。
- Mobが広範囲に散らばると見えなくなるため、-32~32の正方形領域を柵で囲み、視認性を確保。

