Gemini Advancedでコーディングを試す.2
以前の投稿でGemini Advanced のコーディング能力を試すためにWeasel Programを組んだ.今回はLangton’s Antを組んでみたいと思う.
プロンプトと出力結果は下記.
Langton's AntのシミュレーションをPythonで実装.
商品として販売できるくらい表現をリッチにして.
GIFで出力して.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from matplotlib.colors import ListedColormap
import imageio
import os
import time
import shutil # ディレクトリ削除用
# --- 設定 ---
GRID_SIZE = 150 # グリッドの一辺のサイズ (奇数にすると中央が明確)
STEPS = 12000 # シミュレーションの総ステップ数
INIT_POS = (GRID_SIZE // 2, GRID_SIZE // 2) # アリの初期位置 (中央)
INIT_DIRECTION = 0 # アリの初期向き (0:上, 1:右, 2:下, 3:左)
INTERVAL = 5 # アニメーションのフレーム間隔 (ミリ秒)。小さいほど速い
FILENAME = 'langtons_ant_rich_simulation.gif' # 出力GIFファイル名
CMAP_COLORS = ['#FFFFFF', '#2c3e50'] # グリッドの色 (例: 白, ダークブルー)
# CMAP_COLORS = ['white', 'black'] # シンプルな白黒
ANT_COLOR = '#e74c3c' # アリの色 (例: レッド)
ANT_SIZE = 8 # アリのマーカーサイズ
SAVE_FRAME_INTERVAL = 1 # 何ステップごとにフレームを保存するか (1なら全フレーム)
TEMP_DIR = "temp_langton_frames" # 一時画像保存用ディレクトリ名
# --- 初期化 ---
# グリッドを0で初期化 (CMAP_COLORS[0]の色に対応)
grid = np.zeros((GRID_SIZE, GRID_SIZE), dtype=int)
ant_pos = list(INIT_POS)
ant_dir = INIT_DIRECTION
# 方向ベクトル (行, 列): 上, 右, 下, 左
directions = [(-1, 0), (0, 1), (1, 0), (0, -1)]
# 向きに応じたマーカー: 上向き(^), 右向き(>), 下向き(v), 左向き(<)
ant_markers = ['^', '>', 'v', '<']
frame_files = [] # GIF用のフレームファイルパスを保存するリスト
# --- 描画の準備 ---
fig, ax = plt.subplots(figsize=(10, 10)) # 少し大きめの図で詳細を表示
cmap = ListedColormap(CMAP_COLORS)
norm = plt.Normalize(vmin=0, vmax=len(CMAP_COLORS)-1) # カラーマップの正規化
# 一時ディレクトリの作成 (存在すれば削除して再作成)
if os.path.exists(TEMP_DIR):
shutil.rmtree(TEMP_DIR)
os.makedirs(TEMP_DIR)
print(f"--- Langton's Ant Simulation ---")
print(f"Grid Size: {GRID_SIZE}x{GRID_SIZE}")
print(f"Steps: {STEPS}")
print(f"Output GIF: {FILENAME}")
print(f"---------------------------------")
print(f"シミュレーション開始...")
start_time = time.time()
# --- シミュレーションループ ---
try:
for step in range(STEPS):
# 1. 境界チェック
if not (0 <= ant_pos[0] < GRID_SIZE and 0 <= ant_pos[1] < GRID_SIZE):
print(f"\nエラー: ステップ {step} でアリがグリッド境界外 ({ant_pos}) に到達しました。")
print("シミュレーションを早期終了します。")
print("グリッドサイズを大きくするか、ステップ数を減らすことを検討してください。")
break # ループを抜ける
# 2. 現在のマスの色を取得
current_color_index = grid[ant_pos[0], ant_pos[1]]
# 3. ルールに従って向きを変更
if current_color_index == 0: # CMAP_COLORS[0] (例: 白) の場合
ant_dir = (ant_dir + 1) % 4 # 右に90度回転
else: # CMAP_COLORS[1] (例: 黒) の場合
ant_dir = (ant_dir - 1 + 4) % 4 # 左に90度回転
# 4. 現在のマスの色を反転 (0 -> 1, 1 -> 0)
grid[ant_pos[0], ant_pos[1]] = 1 - current_color_index
# 5. アリを1マス進める
move = directions[ant_dir]
ant_pos[0] += move[0]
ant_pos[1] += move[1]
# --- フレームの描画と保存 ---
if step % SAVE_FRAME_INTERVAL == 0:
ax.clear() # 前のフレームの内容をクリア
# グリッドを描画 (extentで座標軸を合わせる, origin='upper'で左上原点)
ax.imshow(grid, cmap=cmap, norm=norm, origin='upper',
extent=[0, GRID_SIZE, GRID_SIZE, 0])
# アリの位置と向きをマーカーで表示
# imshowのextentに合わせるため、y座標を反転させる
ax.plot(ant_pos[1] + 0.5, ant_pos[0] + 0.5, # マスの中央にプロット
marker=ant_markers[ant_dir], color=ANT_COLOR,
markersize=ANT_SIZE, linestyle='None') # 線なしマーカーのみ
ax.set_title(f"Langton's Ant - Step: {step + 1}/{STEPS}", fontsize=14)
ax.set_xticks([]) # X軸の目盛りを非表示
ax.set_yticks([]) # Y軸の目盛りを非表示
plt.tight_layout() # レイアウト調整
# フレームを一時ファイルとして保存
frame_path = os.path.join(TEMP_DIR, f"frame_{step:0{len(str(STEPS))}d}.png")
plt.savefig(frame_path, dpi=96) # dpiはGIFの解像度に影響
frame_files.append(frame_path)
# 進捗表示 (100ステップごと)
if (step + 1) % 100 == 0:
elapsed = time.time() - start_time
print(f"\r ステップ {step + 1}/{STEPS} 完了... ({elapsed:.2f}秒経過)", end="")
except Exception as e:
print(f"\nシミュレーション中に予期せぬエラーが発生しました: {e}")
import traceback
traceback.print_exc() # 詳細なエラー情報を表示
finally:
plt.close(fig) # Matplotlibのウィンドウを閉じる
end_time = time.time()
total_time = end_time - start_time
print(f"\nシミュレーション完了。総計算時間: {total_time:.2f}秒")
# --- GIF生成 ---
if frame_files:
print(f"\nGIFアニメーション生成中 ({len(frame_files)} フレーム)...")
gif_start_time = time.time()
try:
with imageio.get_writer(FILENAME, mode='I', duration=INTERVAL/1000.0, loop=0) as writer:
# loop=0で無限ループ
for i, filename in enumerate(frame_files):
image = imageio.v2.imread(filename) # v2 APIを使用
writer.append_data(image)
if (i + 1) % 50 == 0: # GIF生成の進捗表示
print(f"\r GIFにフレーム {i + 1}/{len(frame_files)} を追加中...", end="")
gif_end_time = time.time()
print(f"\nGIF生成完了: {FILENAME} ({gif_end_time - gif_start_time:.2f}秒)")
except Exception as e:
print(f"\nGIF生成中にエラーが発生しました: {e}")
import traceback
traceback.print_exc()
# 一時ファイルの削除
print("一時ファイルを削除中...")
try:
shutil.rmtree(TEMP_DIR)
print("一時ファイルを削除しました。")
except Exception as e:
print(f"一時ファイルの削除中にエラーが発生しました: {e}")
else:
print("\n保存されたフレームがないため、GIFは生成されませんでした。")
print("\n処理が完了しました。")