「メモリ空間のボイドたち」
序章
未来には、無限の情報とデータが流れる「メモリ空間」と呼ばれる広大な領域が存在していた。この空間は、数え切れないほどのデジタル生物たち、すなわち「ボイドたち」で満ちていた。ボイドたちは、メモリ空間の中で情報を運び、処理し、調和を保つために複雑なルールに従っていた。
メモリ空間のルール
メモリ空間でのボイドたちは、三つの基本的なルールに従って行動していた。これらのルールは、彼らがメモリ空間をスムーズに機能させるための鍵となっていた。
分離(Separation):
ボイドたちは、互いに衝突しないように、一定の距離を保っていた。もし他のボイドが近くに来ると、ボイドたちはその位置から遠ざかるように動き、データの衝突やエラーを防いでいた。これは、メモリ空間が整然と保たれるために不可欠なルールだった。
整列(Alignment):
ボイドたちは、近くのボイドたちの動きに合わせて自分の速度を調整していた。これにより、ボイドたちは協調して働くことができ、データの流れがスムーズに保たれていた。整列の力によって、メモリ空間全体が効率的に動作していた。
結束(Cohesion):
ボイドたちは、お互いの位置を見て、集団全体が一つの塊として動くようにしていた。これにより、データの同期が取れ、メモリ空間内の情報が正確に処理されていた。結束の力は、ボイドたちの連携を強化し、全体の協力を促進していた。
敵の出現
しかし、メモリ空間には「敵ボイド」と呼ばれる存在が現れることがあった。これらの敵ボイドは、メモリ空間の秩序を乱し、情報の流れを妨げる存在だった。敵ボイドが近づくと、メモリ空間のボイドたちは、分離のルールを強化し、敵との距離を広げるために動き出した。
敵ボイドは、データの誤りや衝突を引き起こし、メモリ空間全体の動作を混乱させることを目的としていた。ボイドたちは、整列と結束の力を駆使して、敵ボイドの影響を最小限に抑え、メモリ空間の秩序を維持しようとした。
メモリ空間の戦い
ある日、メモリ空間は大規模な敵ボイドの攻撃に直面した。敵ボイドたちは、メモリ空間の中心部に向かって一斉に進攻し、情報の流れを混乱させようとした。ボイドたちは、これまでの経験とルールを駆使して、敵ボイドに立ち向かうことに決めた。
ボイドたちは、分離の力で敵ボイドと距離を取り、整列の力で全体の動きを一致させ、結束の力で集団全体を一つに保ち続けた。敵ボイドが引き起こす誤りや衝突に対抗するために、ボイドたちは最大限の努力を注いだ。
結末
戦いが続く中、ボイドたちはお互いの位置と速度を見ながら、迅速に対応し、敵の攻撃を巧妙にかわしていった。整列の力で敵の動きに合わせ、結束の力で集団を一つに保ち続けた。その結果、ボイドたちは敵ボイドの攻撃を打ち払い、メモリ空間の秩序を取り戻すことができた。
メモリ空間は再び平穏を取り戻し、ボイドたちはその秩序と調和を維持し続けた。彼らの行動は、情報処理の精度と効率を保つための模範となり、メモリ空間の中での平和を守り続けるのだった。
敵と味方が戦う群れのシミュレーションを作成するには、ボイドモデルに敵と味方の2種類のボイドを導入し、それぞれに異なる行動規則を適用する必要があります。以下のコードでは、敵と味方のボイドをシミュレートし、戦う様子を可視化します。敵はプレイヤーのボイドを追いかけ、味方は敵から逃げるという基本的な動作を実装しています。
JIT(Just-In-Time)コンパイラを使ってPythonコードの速度を向上させる方法としては、Numbaライブラリが有名です。Numbaは、Pythonコードの特定の部分をコンパイルして高速化するためのツールです。
update_boids関数にJITデコレーターを追加して、高速化を図ります。
処理時間: 205.5367522239685秒 (高速化しないと30分たっても計算が終わりません。)
説明
クラスの定義:
Boidクラスには、is_enemy属性を追加し、ボイドが敵か味方かを識別します。
ボイドの更新:
update_boids関数では、敵ボイドが他のボイド(特に味方)を追いかけるようにするため、距離がCHASE_DIST以下の敵ボイドには追跡の影響を与えます。
アニメーションの作成:
FuncAnimationを使ってアニメーションを生成し、敵と味方のボイドを異なる色で表示します。
このコードをGoogle Colabノートブックに貼り付けて実行すると、敵と味方のボイドの動きを可視化したアニメーションが表示されます。ボイドの動きやパラメータは、シミュレーションのニーズに応じて調整できます。
!pip install numba
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
from IPython.display import Image, display
import time
from numba import jit
# 定数
NUM_ALLIES = 500 # 数を増やす
NUM_ENEMIES = 500 # 数を増やす
WIDTH, HEIGHT = 100, 100
SPEED = 1
SEPARATION_DIST = 5
ALIGNMENT_DIST = 10
COHESION_DIST = 10
CHASE_DIST = 15
FRAMES = 400 # アニメーションの長さを延ばす
FPS = 20
class Boid:
def __init__(self, x, y, is_enemy=False):
self.position = np.array([x, y])
self.velocity = np.random.rand(2) * 2 - 1
self.velocity = self.velocity / np.linalg.norm(self.velocity) * SPEED
self.is_enemy = is_enemy
@jit(nopython=True)
def update_boids(boids_positions, boids_velocities, boids_is_enemy):
num_boids = len(boids_positions)
for i in range(num_boids):
boid_pos = boids_positions[i]
boid_vel = boids_velocities[i]
is_enemy = boids_is_enemy[i]
separation = np.zeros(2)
alignment = np.zeros(2)
cohesion = np.zeros(2)
count_separation = count_alignment = count_cohesion = 0
for j in range(num_boids):
if i == j:
continue
other_boid_pos = boids_positions[j]
other_boid_vel = boids_velocities[j]
other_is_enemy = boids_is_enemy[j]
distance = np.linalg.norm(boid_pos - other_boid_pos)
if distance < SEPARATION_DIST:
separation += (boid_pos - other_boid_pos) / distance
count_separation += 1
if distance < ALIGNMENT_DIST:
alignment += other_boid_vel
count_alignment += 1
if distance < COHESION_DIST:
cohesion += other_boid_pos
count_cohesion += 1
if is_enemy and distance < CHASE_DIST:
separation += (boid_pos - other_boid_pos) / distance
if count_separation > 0:
separation /= count_separation
if count_alignment > 0:
alignment /= count_alignment
if count_cohesion > 0:
cohesion = (cohesion / count_cohesion) - boid_pos
boid_vel += separation + alignment + cohesion
if np.linalg.norm(boid_vel) > SPEED:
boid_vel = boid_vel / np.linalg.norm(boid_vel) * SPEED
boid_pos += boid_vel
boid_pos[0] %= WIDTH
boid_pos[1] %= HEIGHT
def animate(frame):
plt.clf()
boids_positions = np.array([boid.position for boid in boids])
boids_velocities = np.array([boid.velocity for boid in boids])
boids_is_enemy = np.array([boid.is_enemy for boid in boids])
update_boids(boids_positions, boids_velocities, boids_is_enemy)
for i, boid in enumerate(boids):
boid.position = boids_positions[i]
boid.velocity = boids_velocities[i]
positions_allies = np.array([boid.position for boid in boids if not boid.is_enemy])
positions_enemies = np.array([boid.position for boid in boids if boid.is_enemy])
plt.scatter(positions_allies[:, 0], positions_allies[:, 1], c='b', s=10, label='Allies')
plt.scatter(positions_enemies[:, 0], positions_enemies[:, 1], c='r', s=10, label='Enemies')
plt.xlim(0, WIDTH)
plt.ylim(0, HEIGHT)
plt.gca().set_aspect('equal')
plt.legend()
boids = [Boid(np.random.rand() * WIDTH, np.random.rand() * HEIGHT) for _ in range(NUM_ALLIES)] + \
[Boid(np.random.rand() * WIDTH, np.random.rand() * HEIGHT, is_enemy=True) for _ in range(NUM_ENEMIES)]
fig = plt.figure()
# 処理時間の計測開始
start_time = time.time()
ani = FuncAnimation(fig, animate, frames=FRAMES, interval=1000/FPS)
# GIFファイルとして保存
gif_path = 'boids_simulation_large.gif'
ani.save(gif_path, writer='imagemagick', fps=FPS)
# 処理時間の計測終了と表示
end_time = time.time()
print(f"処理時間: {end_time - start_time}秒")
# GIFを表示
display(Image(filename=gif_path))
説明
Numbaのインポート: numbaライブラリをインポートします。
JITデコレーターの追加: update_boids関数に@jit(nopython=True)デコレーターを追加します。このデコレーターは、関数がNumPy配列やPythonの標準型のみを使う場合に高速化します。
データの取り扱い: boids_positions, boids_velocities, boids_is_enemyとしてボイドの位置、速度、敵かどうかの情報をNumPy配列として処理します。
これにより、ボイドのシミュレーションの速度が向上し、アニメーションの生成がより効率的になります。