o3-mini-highを試してみる.
以前の投稿でo3-mini-highについてまとめた.
今回はo3-mini-highでどこまでできるかという点で下記のプロンプトを投げてみた.
o3-mini-high じゃないと作れないような,高度で有意義な人工生命プログラムのシミュレーターをPythonで書いて.
最終的にはアニメーションのGIFで出力して.
すると,あるシミュレーションを出してくれた.内容とコードと結果は下記.
- シミュレーション空間
- 100 × 100 の2次元グリッド
- エージェント
- 初期数: 50体
- 属性: 位置、エネルギー、遺伝子(移動速度
speed
と感知範囲sense_range
)
- 挙動:
- 周囲の資源を感知して移動(なければランダムウォーク)
- 移動時にエネルギーを消費
- 資源に近づくと資源を摂取してエネルギー獲得
- エネルギーが一定値(100)を超えると複製(エネルギーを半分に分割し、遺伝子に微小変異を付与)
- 資源
- 初期数: 200個
- 各シミュレーションステップごとに5個の新規資源が生成
- エージェントが一定距離(2.0未満)に近づくと摂取される
- シミュレーションループ
- 全体200ステップで進行
- 各ステップでエージェントの移動、摂取、複製、エネルギー切れによる削除が行われる
- 可視化
- 各ステップごとに、エージェントと資源の位置を matplotlib でプロット
- プロット結果をフレームとして保存し、最終的に animated GIF として出力
import numpy as np
import matplotlib.pyplot as plt
import imageio
import random
# シミュレーションのパラメータ設定
WIDTH, HEIGHT = 100, 100 # シミュレーション空間のサイズ
NUM_AGENTS = 50 # 初期エージェント数
INITIAL_RESOURCES = 200 # 初期資源数
SIMULATION_STEPS = 200 # シミュレーションの総ステップ数
RESOURCE_REGEN_RATE = 5 # 各ステップで追加される資源数
# エージェントクラス:各エージェントは位置、エネルギー、そして行動パラメータ(遺伝子)を持つ
class Agent:
def __init__(self, x, y, energy, genome=None):
self.x = x
self.y = y
self.energy = energy
# genome: 'speed'(移動速度)と 'sense_range'(資源感知範囲)を持つ
if genome is None:
self.genome = {
'speed': random.uniform(0.5, 2.0),
'sense_range': random.uniform(5, 15)
}
else:
self.genome = genome
def distance_to(self, x, y):
return np.sqrt((self.x - x)**2 + (self.y - y)**2)
def move(self, resources):
"""
エージェントは周囲の資源を感知し、もし感知範囲内にあればその方向に移動する。
なければランダムウォークを行う。
移動にはエネルギー消費が伴う。
"""
target = None
min_dist = float('inf')
# 感知範囲内にある最も近い資源を探索
for rx, ry in resources:
d = self.distance_to(rx, ry)
if d < self.genome['sense_range'] and d < min_dist:
min_dist = d
target = (rx, ry)
if target:
# 資源の方向に向かって正規化したベクトルで移動
dx = target[0] - self.x
dy = target[1] - self.y
norm = np.sqrt(dx * dx + dy * dy)
if norm != 0:
dx /= norm
dy /= norm
self.x += dx * self.genome['speed']
self.y += dy * self.genome['speed']
else:
# ランダムウォーク
angle = random.uniform(0, 2 * np.pi)
self.x += np.cos(angle) * self.genome['speed']
self.y += np.sin(angle) * self.genome['speed']
# シミュレーション空間の境界で反射するようにクリップ
self.x = np.clip(self.x, 0, WIDTH)
self.y = np.clip(self.y, 0, HEIGHT)
# 移動によるエネルギー消費(移動速度に比例)
self.energy -= 0.5 * self.genome['speed']
def try_eat(self, resources):
"""
エージェントが資源に十分近づいた場合、資源を摂取しエネルギーを獲得する。
資源との距離が 2.0 未満なら摂取対象とする。
"""
eaten = None
for i, (rx, ry) in enumerate(resources):
if self.distance_to(rx, ry) < 2.0:
self.energy += 20 # 資源を摂取すると 20 エネルギーを獲得
eaten = i
break
if eaten is not None:
del resources[eaten]
def try_reproduce(self):
"""
エージェントのエネルギーが一定値(ここでは 100)を超えた場合、エネルギーを半分に分けて複製を行う。
複製時に genome の各パラメータに微小な変異を付与して遺伝的多様性を生む。
"""
if self.energy > 100:
self.energy /= 2 # 親と子でエネルギーを分割
new_genome = {
'speed': self.genome['speed'] * random.uniform(0.95, 1.05),
'sense_range': self.genome['sense_range'] * random.uniform(0.95, 1.05)
}
# 子エージェントは親の近傍に生成
offset = random.uniform(-2, 2)
child = Agent(self.x + offset, self.y + offset, self.energy, new_genome)
return child
return None
# 初期状態の生成
agents = []
for _ in range(NUM_AGENTS):
x = random.uniform(0, WIDTH)
y = random.uniform(0, HEIGHT)
agents.append(Agent(x, y, energy=50))
resources = []
for _ in range(INITIAL_RESOURCES):
x = random.uniform(0, WIDTH)
y = random.uniform(0, HEIGHT)
resources.append((x, y))
# シミュレーションの各ステップごとにフレームを保存していく
frames = []
for step in range(SIMULATION_STEPS):
new_agents = []
# 各エージェントの更新
for agent in agents:
agent.move(resources)
agent.try_eat(resources)
child = agent.try_reproduce()
if child:
new_agents.append(child)
agents.extend(new_agents)
# エネルギーが尽きたエージェントは削除(死滅)
agents = [agent for agent in agents if agent.energy > 0]
# 各ステップごとに新たな資源をランダムに生成
for _ in range(RESOURCE_REGEN_RATE):
x = random.uniform(0, WIDTH)
y = random.uniform(0, HEIGHT)
resources.append((x, y))
# 現在の状態をプロットして画像として保存
fig, ax = plt.subplots(figsize=(6, 6))
ax.set_xlim(0, WIDTH)
ax.set_ylim(0, HEIGHT)
ax.set_title(f"Step {step+1}")
# 資源を緑色の点で描画
if resources:
res_arr = np.array(resources)
ax.scatter(res_arr[:, 0], res_arr[:, 1], color='green', s=10, label='Resources', alpha=0.6)
# エージェントをエネルギーに応じた色(hot カラーマップ)で描画
agent_x = [agent.x for agent in agents]
agent_y = [agent.y for agent in agents]
energies = [agent.energy for agent in agents]
sc = ax.scatter(agent_x, agent_y, c=energies, cmap='hot', s=30, label='Agents', edgecolors='black')
ax.legend(loc='upper right')
# フレームをメモリ上に保存
fig.canvas.draw()
# RGBA バッファを取得
buffer = fig.canvas.buffer_rgba()
# NumPy 配列に変換(形状は (height, width, 4))
image = np.asarray(buffer)
# アルファチャンネルを除いて RGB のみを取り出す
image = image[...,:3]
frames.append(image)
# 保存したフレームから animated GIF を作成(fps=5)
output_filename = "artificial_life_simulation.gif"
imageio.mimsave(output_filename, frames, fps=5)
print(f"Simulation complete. GIF saved as {output_filename}")
