ラングトンのアリをよりリッチにする.
以前の投稿でラングトンのアリについてまとめた.
今回はさらにプログラムを発展させて,アリの数を増やしてみた.
コードと実行結果は下記.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Langton's Ant シミュレーション(複数アリ版)
・グリッド上のセルは 0(ライトカラー)と 1(ダークカラー)の2状態
・各アリは、現在のセルが白の場合は右90°回転、黒の場合は左90°回転し、セルの色を反転して前進する
・3匹のアリが同時にシミュレーションを行い、アリごとに異なる色(例: 赤、緑、青)で描画される
・シミュレーションの様子を高品質な画像にキャプチャし、GIF ファイルとして出力する
"""
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap
import imageio
import os
def run_multi_ant_simulation(
grid_size=300, # グリッドのサイズ(セル数)
num_steps=30000, # シミュレーションの総ステップ数
frame_interval=50, # 何ステップごとにフレームをキャプチャするか
fps=20, # GIF のフレームレート
output_filename="langtons_ant_multi.gif"
):
# グリッド初期化(0: ライトセル、1: ダークセル)
grid = np.zeros((grid_size, grid_size), dtype=np.uint8)
# 複数のアリの初期設定(位置、向き、色)
# ※各アリは辞書で管理し、"active" が True のときのみ更新される
ants = [
{"y": grid_size // 2, "x": grid_size // 2, "dir": 0, "color": "red", "active": True},
{"y": grid_size // 2, "x": grid_size // 2 + 10, "dir": 1, "color": "green", "active": True},
{"y": grid_size // 2 + 10, "x": grid_size // 2, "dir": 2, "color": "blue", "active": True},
]
# 各方向への移動量(0: 上, 1: 右, 2: 下, 3: 左)
dy = [-1, 0, 1, 0]
dx = [0, 1, 0, -1]
# キャプチャしたフレームを保存するリスト
frames = []
# カスタムカラーマップ(セルの表示色)
cmap = ListedColormap(["#e0e0e0", "#333333"])
# シミュレーションループ
for step in range(num_steps):
# 各アリについてルールに従って更新
for ant in ants:
if not ant["active"]:
continue # グリッド外に出たアリは更新しない
y, x, d = ant["y"], ant["x"], ant["dir"]
current_color = grid[y, x]
if current_color == 0:
# 白セルの場合:右90°回転、セルを黒に反転
ant["dir"] = (d + 1) % 4
grid[y, x] = 1
else:
# 黒セルの場合:左90°回転、セルを白に反転
ant["dir"] = (d - 1) % 4
grid[y, x] = 0
# 新しい位置を計算
new_y = y + dy[ant["dir"]]
new_x = x + dx[ant["dir"]]
# 境界チェック: グリッド外へ出た場合はアリを非アクティブにする
if new_y < 0 or new_y >= grid_size or new_x < 0 or new_x >= grid_size:
ant["active"] = False
else:
ant["y"] = new_y
ant["x"] = new_x
# すべてのアリが非アクティブになった場合、シミュレーションを終了
if not any(ant["active"] for ant in ants):
print(f"すべてのアリがグリッド外に出ました (step: {step})。シミュレーションを終了します。")
break
# 一定間隔でフレームをキャプチャ
if step % frame_interval == 0:
# 高品質な図を作成(背景色やレイアウトにこだわる)
fig, ax = plt.subplots(figsize=(6, 6), dpi=100)
fig.patch.set_facecolor('#f0f0f0')
ax.set_facecolor('#f0f0f0')
# グリッドの状態を描画
ax.imshow(grid, cmap=cmap, interpolation='nearest')
# 各アリの現在位置を、個別の色で描画
for ant in ants:
if ant["active"]:
ax.plot(ant["x"], ant["y"],
marker='o', color=ant["color"],
markersize=8, markeredgecolor='black',
markeredgewidth=1)
# ステップ数を左上に注釈表示
ax.text(0.02, 0.95, f"Step: {step}", transform=ax.transAxes,
color='blue', fontsize=12, fontweight='bold',
bbox=dict(facecolor='white', alpha=0.8, edgecolor='none'))
# 軸は非表示
ax.set_xticks([])
ax.set_yticks([])
plt.tight_layout()
fig.canvas.draw()
# キャンバスのRGBAバッファからRGB部分のみを抽出して画像に変換
width, height = fig.canvas.get_width_height()
image = np.frombuffer(fig.canvas.buffer_rgba(), dtype=np.uint8)
image = image.reshape((height, width, 4))[:, :, :3] # アルファチャンネルを除去
frames.append(image)
plt.close(fig)
# キャプチャしたフレームを GIF として保存
if frames:
imageio.mimsave(output_filename, frames, fps=fps)
print(f"GIFが正常に保存されました: {os.path.abspath(output_filename)}")
else:
print("キャプチャされたフレームがありません。GIFは作成されませんでした。")
if __name__ == '__main__':
run_multi_ant_simulation()

参考文献
- Langton, C. G. (1986). Studying artificial life with cellular automata. Physica D: nonlinear phenomena, 22(1-3), 120-149.