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}")

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です