どうやらシムアースのネット対応版と思えばよさそうです。「テラリウム」をインストールすると、誰もいない、あまり広くない土地が現れました。
えさをさがすアルゴリズム。いいタイミングで子供を産めるアルゴリズム。積極的に他人のパソコンにワープできるアルゴリズム。いろんなパソコンにワープしてこそ、個体数を増やせるというものです。すぐれたアルゴリズムを持つ生物は、あっというまに増えて世界中のパソコンに住み着くんですから、まるでウィルスです。
いまちょうどコンテスト期間で、6 月 28 日の時点でピア全体で一番個体数が多い上位100名が100 人が賞品をもらえるそうです。最優秀 1位〜10位にはなんと X-BOX だそうです!・・・あんまりうれしくない? まぁどんなもんなのか試してみましょう。。
な〜んにも買わなくても、.NET Framework SDK をダウンロードしてきてインストールすれば C# のコンパイラがもれなくついてるので、「秀丸エディタ」でプログラムを書いて、コンパイルさせれば「テラリウム」で遊ぶにはじゅうぶんそうです。
ちなみに、Microsoft は C# でプログラミングするように推奨しているようなので、その方向でいきます。
動物の種は、つぎの7種類の基本能力を持っています。いわゆるステータス。。
あとソースに書いてる固有情報は、「種の名前」「作者の名前」「作者のアドレス」「動物の外見」「動物のサイズ」。動物のサイズってのがよくわかんないですが。。まぁいいや。
成長するだけのエネルギーがあって、成長するだけのスペースがまわりにあれば、その瞬間、勝手に成長します。
アクションについては、こんな感じでプログラミングします…。奥がふかそうです。
生物には、テレポーターの位置を知るような関数は用意されていないので、自ら積極的にテレポーテーションしようと思ったら、かなり難しい。自分の周囲を眺めて、ある生物が突然いなくなったら、それはテレポーターによって移動したのかもしれない、という判断でテレポーターの位置を知るしかない。
さて、昨日はプログラミングのことはいっさい書かなかったので、今日は一気にプログラミング寄りな話題になります。自分の種を作りたいときにどうするんでしょうか。
定数の宣言;
public class MyCreature : Animal
{
動物の中身
}
なんて書いてあって、ひとつしかない MyCreature クラスの中に、動物のふるまいが全部おさめられています。
このコードをコンパイルして dll にしたものが、生物の種のファイルになります。テラリウム上から「種の導入」を押してから、この dll を選ぶと、自分の作った種を10匹送り込むことができます。あとはがんばって増えてくれますように…。
// ベース ポイントの属性 // 合計100ポイントを下記のいずれかに割り振ります。 [MaximumEnergyPoints(0)] // 体力 [EatingSpeedPoints(0)] // 植物を食べる速さ [AttackDamagePoints(0)] // 攻撃力 [DefendDamagePoints(10)] // 防御力 [MaximumSpeedPoints(0)] // 移動の速度 [CamouflagePoints(30)] // 偽装(身を隠す)能力 [EyesightPoints(60)] // 視野の広さなんて書いてあるのは、合計100ポイントの基本能力を振り分けているところですね。どうもこのサンプル草食動物は、視野だけ広くて、あとの能力はほとんどありません。
さて、その次にはクラスの本体がかかれています。はしょって書くと、MyCreature クラスの全体像はこんな感じです。
public class MyCreature : Animal
{
PlantState targetPlant = null;
// イベントハンドラの登録
protected override void Initialize() { ... }
// イベントハンドラ
void LoadEvent() { ... } // ロード時最初に呼び出される
void IdleEvent() { ... } // 定期的に呼び出される
void AttackedEvent (e) { ... } // 攻撃を受けた時に防御のために呼び出される
// 視界内のえさを探すルーチン
bool ScanForTargetPlant() { ... }
// ここより下はテラリウムを中断および再開させる際に実行されます
public override void SerializeAnimal() { ... }
public override void DeserializeAnimal() { ... }
}
ざーっと見たところ、ここで核になっているのが IdelEvent() で、生物がヒマで特にすることがないときに呼ばれるようです。このサンプルでは、ヒマなときにこういうアルゴリズムで行動しています。
こんな生物は、生存競争の激しい場所では生き残れなさそうです。実際、10匹放ったところ、他のプレイヤーの生物にあっというまに絶滅させられてしまいました。ここからどう改良するのか…。
Animal クラスのメソッド一覧
Void BeginAttacking(AnimalState) // 攻撃命令 Void BeginDefending(AnimalState) // 防御命令 Void BeginEating(OrganismState) // 摂食命令 Void BeginMoving(MovementVector) // 移動命令 Void DeserializeAnimal(MemoryStream) Void InternalMain() Boolean IsMySpecies(OrganismState) // 相手生物が、自分と同種族か判定 OrganismState LookFor(OrganismState) OrganismState RefreshState(String) ArrayList Scan() // 視界内をサーチして全ての生物を得る。 Void SerializeAnimal(MemoryStream) Void StopMoving() // 移動中止命令 Boolean WithinAttackingRange(AnimalState) //対象が、攻撃可能範囲にいるか? Boolean WithinEatingRange(OrganismState) //対象が、摂食可能範囲にいるか?Animal クラスのプロパティ一覧
Boolean CanEat { get; } // 満腹か、そうでないか
AttackAction CurrentAttackAction { get; } // 現在の攻撃状態
DefendAction CurrentDefendAction { get; } // 現在の防御状態
EatAction CurrentEatAction { get; } // 現在の摂食状態
MoveToAction CurrentMoveToAction { get; } // 現在の移動状態
Boolean IsAttacking { get; } // 攻撃中か、そうでないか
Boolean IsDefending { get; } // 防御中か、そうでないか
Boolean IsEating { get; } // 摂食中か、そうでないか
Boolean IsMoving { get; } // 移動中か、そうでないか
IAnimalSpecies Species { get; } // 生物の種情報を得る
AnimalState State { get; } // 生物の現在の状態を得る
Int32 WorldHeight { get; } // 世界の縦幅を得る
Int32 WorldWidth { get; } // 世界の横幅を得る
あ゛〜。いっぱいですわ。
[OrgStt].ActualDirection // 移動の方向 [AniStt].AnimalSpecies // 生物の種に関する情報 [??????].BitmapRadius [??????].BitmapWidth [OrgStt].CellRadius // 半径(グリッド数) [Animal].CurrentMobeToAction // 移動の状況:方向と速度 [Organi].CurrentReproduceAction // 生殖の状況 [AniStt].Damage // ダメージの絶対量 [OrgStt].DeathReason // 死んだ理由 [OrgStt].EnergyState // 現在のエネルギー状態 [OrgStt].FoodChunks // 食料値 [OrgStt].Generation // 世代番号 [??????].GridX // 現在位置 X (グリッド単位) [??????].GridY // 現在位置 Y (グリッド単位) [ISpeci].GrowthWait // 成長するまでの待ちティック数 [OrgStt].ID // EcoSystem 内でのこの生物の ID [OrgStt].IncubationTicks // 生殖を終了するまでの待ちティック数 [OrgStt].IsAlive // 生きているのか死んでいるのか [??????].IsImmutable [OrgStt].IsIncubating // 生殖の過程にあるのかどうか [OrgStt].IsMature // 成熟しているかどうか [OrgStt].IsStopped // 停止しているかどうか [??????].OrganismEvents [OrgStt].PercentEnergy // 現在持っているエネルギーの% [AniStt].PercentInjured // これまでに受けたダメージの% [OrgStt].PercentLifespanRemaining // 寿命% [OrgStt].Position // 現在位置 [OrgStt].Position.X // 現在位置 X [OrgStt].Position.Y // 現在位置 Y [??????].PreviousDisplayAction [OrgStt].Radius // 生物の半径 [OrgStt].ReadyToReproduce // 生殖を行う準備ができているかどうか [??????].RenderInfo [ISpeci].ReproductionWait // 生殖するための待ちティック数 [AniStt].RotTicks // 死んでから経過したティック数 [Animal].Species // 生物の不変特性 [IAnisp].Species.EatingSpeedPerUnitRadius // 単位半径当たり1ティックで食べれる食料 [IAnisp].Species.EyesightRadius // 視界。見ることができる距離 [ISpeci].Species.GrowthWait // 成長するまでの待ちティック数 [??????].Species.IntialRadius // ??? 初期半径?みんな 11 だ。 [IAnisp].Species.InvisibleOdds // Scan メソッドを使用する他の生物から見えずにいられる確率 [IAnisp].Species.IsCarnivore // 肉食動物であるか? [ISpeci].Species.LifeSpan // 寿命ティック数 [??????].Species.MarkingColor [ISpeci].Species.MatureRadius // 完全に成長したときの半径 [IAnisp].Species.MaximumAttackDamagePerUnitRadius // 単位半径当たり1ティックで与えうる最大ダメージ [IAnisp].Species.MaximumDefendDamagePerUnitRadius // 単位半径当たり1ティックで吸収しうる最大ダメージ [ISpeci].Species.MaximumEnergyPerUnitRadius // 単位半径当たりに貯蔵できる最大エネルギー [IAnisp].Species.MaximumSpeed // 最大移動速度 [??????].Species.Name [ISpeci].Species.ReproductionWait // 生殖するための待ちティック数。 [ISpeci].Species.Skin // スキンを識別する文字列 [IAnisp].Species.SkinFamily // スキン ファミリーを識別する文字列 [MovementVector].Speed // 移動速度 [OrgStt].StoredEnergy // 貯蔵エネルギーの量 [OrgStt].TickAge // 年齢ティック数 サフィックスの説明 対応するクラスから、アクセッサーを通じて値を読むことができる。 [Organi]. // Organism クラス [Animal]. // Animal クラス [OrgStt]. // OrganismState クラス [AniStt]. // AnimalState クラス [ISpeci]. // ISpecies クラス [IAnisp]. // IAnimalSpecies クラスふ〜。植物の場合は、また微妙に違いますよん。
ArrayList foundCreatures = Scan(); // 生物を探して情報を配列に格納
if(foundCreatures.Count > 0) // 発見された生物は0より大きい: はい
{
// 配列内を走査
foreach(OrganismState organismState in foundCreatures)
{
if(organismState is PlantState) // その中に植物はあるか?
{
targetPlant = (PlantState) organismState;
// 植物の場所に移動開始
BeginMoving(new MovementVector(organismState.Position, 2));
return true;
}
}
}
へえ〜、C#って foreach があるんですね。うほほ。foreach(変数 in 配列) という文法で、配列の中身が順番に値渡しされて、処理できるようです。値渡しっていうのがポイントで、C#の foreach は配列の中身までは書き換わりません。
次に、テラリウムをリセット後、肉食動物のサンプルコードをビルドして野に放ったら、ワーニングが出ました。「Points applied to 'DefendDamagePointsAttribute' should be in increments of 4. Anything else is wasted.」ん〜? DefendDamagePoints は4おきに設定しないと、無駄が出るよ、ってことでしょうか。サンプルコードのくせに〜〜〜。
ワーニングは出ても、ちゃんと10匹が野に放たれました。このままぼーっと見てると、やがてともぐいをはじめました。勝った奴はまるまると太りましたが、他にえさもないので餓死です。気づいたこと