衚珟力豊かなJavaScript電子生掻プロゞェクト

内容




車が考えるこずができるかどうかの問題は、朜氎艊が航行できるかどうかの問題ず同じくらい適切です。

Edsger Dijkstra、コンピュヌティングサむ゚ンスぞの脅嚁

プロゞェクトの章では、理論を投げるのをやめ、プログラムに぀いお䞀緒に取り組みたす。 理論はプログラミングの教育に䞍可欠ですが、重芁なプログラムを読んで理解する必芁がありたす。

私たちのプロゞェクトは、移動しお生存のために戊う生き物が生息する小さな䞖界である仮想゚コシステムの構築です。

定矩


タスクを実行可胜にするために、䞖界の抂念を倧幅に簡玠化したす。 ぀たり、䞖界は2次元のグリッドになり、各゚ンティティが1぀のセルを占有したす。 各タヌンで、クリヌチャヌは䜕らかのアクションを実行できたす。

したがっお、時間ずスペヌスを固定サむズの単䜍に分割したす。぀たり、スペヌスのセルず時間の動きです。 もちろん、これは倧雑把でずさんな近䌌です。 しかし、私たちのシミュレヌションは、面癜くなくお面癜くなければならないので、自由に「コヌナヌをカット」したす。

プランの助けを借りお䞖界を定矩するこずができたす-セルごずに1文字を䜿甚しお䞖界グリッドをレむアりトする文字列の配列。

var plan = ["############################", "# # # o ##", "# #", "# ##### #", "## # # ## #", "### ## # #", "# ### # #", "# #### #", "# ## o #", "# o # o ### #", "# # #", "############################"]; 


蚘号「」は壁ず石を意味し、「o」は生物です。 スペヌスは空のスペヌスです。

蚈画を䜿甚しお、䞖界のオブゞェクトを䜜成できたす。 圌は䞖界の芏暡ず内容を監芖しおいたす。 これには、䞖界を出力ラむンそれが基にしおいるプラ​​ンなどに倉換するtoStringメ゜ッドがあり、内郚で䜕が起こっおいるかを芳察できたす。 ワヌルドのオブゞェクトにはタヌンメ゜ッドがあり、すべおのクリヌチャヌが1぀の動きを行い、アクションに埓っおワヌルドの状態を曎新できたす。

スペヌスを描く


䞖界をモデリングするグリッドには幅ず高さがありたす。 セルは、xおよびy座暙によっお定矩されたす。 これらの座暙ペアを衚すには、単玔なVectorタむプ前の章の挔習からを䜿甚したす。

 function Vector(x, y) { this.x = x; this.y = y; } Vector.prototype.plus = function(other) { return new Vector(this.x + other.x, this.y + other.y); }; 


次に、グリッド自䜓をシミュレヌトするオブゞェクトタむプが必芁です。 グリッドは䞖界の䞀郚ですが、䞖界オブゞェクトを耇雑にしないために、そこから別のオブゞェクト䞖界オブゞェクトのプロパティになりたすを䜜成したす。 䞖界は䞖界に関連するものを自身でロヌドし、グリッドはグリッドに関連するものをロヌドしなければなりたせん。

倀のグリッドを保存するためのいく぀かのオプションがありたす。 文字列配列の配列を䜿甚し、プロパティぞの2段階アクセスを䜿甚できたす。

 var grid = [["top left", "top middle", "top right"], ["bottom left", "bottom middle", "bottom right"]]; console.log(grid[1][2]); // → bottom right 


たたは、幅×高さのサむズの1぀の配列を䜿甚しお、芁玠x、yが䜍眮x +y×widthにあるず刀断できたす。

 var grid = ["top left", "top middle", "top right", "bottom left", "bottom middle", "bottom right"]; console.log(grid[2 + (1 * 3)]); // → bottom right 


アクセスはメッシュオブゞェクトのメ゜ッドにラップされるため、倖郚コヌドはどちらのアプロヌチが遞択されるかを気にしたせん。 アレむを䜜成する方が簡単なので、2番目を遞択したした。 1぀の数倀を匕数ずしおArrayコンストラクタヌを呌び出すず、指定された長さの新しい空の配列が䜜成されたす。

次のコヌドは、メむンメ゜ッドでグリッドオブゞェクトを宣蚀したす。

 function Grid(width, height) { this.space = new Array(width * height); this.width = width; this.height = height; } Grid.prototype.isInside = function(vector) { return vector.x >= 0 && vector.x < this.width && vector.y >= 0 && vector.y < this.height; }; Grid.prototype.get = function(vector) { return this.space[vector.x + this.width * vector.y]; }; Grid.prototype.set = function(vector, value) { this.space[vector.x + this.width * vector.y] = value; }; 


基瀎詊隓

 var grid = new Grid(5, 5); console.log(grid.get(new Vector(1, 1))); // → undefined grid.set(new Vector(1, 1), "X"); console.log(grid.get(new Vector(1, 1))); // → X 


クリヌチャヌプログラミングむンタヌフェむス


ワヌルドのコンストラクタヌを取り䞊げる前に、そこに生息するクリヌチャヌのオブゞェクトを決定する必芁がありたす。 私は、䞖界は生き物に圌らが䜕をしたいのかを尋ねるず述べた。 これは次のように機胜したす。すべおのクリヌチャヌオブゞェクトには、呌び出されたずきにアクションを返すactメ゜ッドがありたす。 アクション-タむププロパティのオブゞェクト。クリヌチャヌが実行したいアクションのタむプ、たずえば「移動」を指定したす。 アクションには、移動方向などの远加情報が含たれる堎合がありたす。

クリヌチャヌはひどく近芖県的であり、生き物のすぐ隣にいる现胞しか芋えたせん。 しかし、これはアクションを遞択するずきに圹立ちたす。 actメ゜ッドが呌び出されるず、ビュヌオブゞェクトが䞎えられ、クリヌチャヌが呚囲の゚リアを探玢できるようになりたす。 8぀の隣接するセルをコンパス方向ず呌びたす。「n」は北、「ne」は北東などです。 方向の名前から座暙オフセットに倉換するために䜿甚されるオブゞェクトは次のずおりです。

 var directions = { "n": new Vector( 0, -1), "ne": new Vector( 1, -1), "e": new Vector( 1, 0), "se": new Vector( 1, 1), "s": new Vector( 0, 1), "sw": new Vector(-1, 1), "w": new Vector(-1, 0), "nw": new Vector(-1, -1) }; 


ビュヌオブゞェクトには、方向を取埗しお文字を返すlookメ゜ッドがありたす。たずえば、壁がある堎合は「」、䜕もない堎合はスペヌスです。 このオブゞェクトは、䟿利なfindおよびfindAllメ゜ッドも提䟛したす。 どちらも、匕数ずしおマップ䞊のものを衚す文字の1぀を取りたす。 最初は、このアむテムがクリヌチャヌの隣で芋぀かる方向を返したす。そのようなアむテムが近くにない堎合はnullを返したす。 2番目は、そのようなオブゞェクトが芋぀かったすべおの可胜な方向を含む配列を返したす。 たずえば、壁の巊偎西偎のクリヌチャヌは、匕数 ""を指定しおfindAllを呌び出すず、["ne"、 "e"、 "se"]を受け取りたす。

これは、障害物に衝突しおランダムな方向に跳ね返るたで歩くだけの単玔なダム生物です。

 function randomElement(array) { return array[Math.floor(Math.random() * array.length)]; } function BouncingCritter() { this.direction = randomElement(Object.keys(directions)); }; BouncingCritter.prototype.act = function(view) { if (view.look(this.direction) != " ") this.direction = view.find(" ") || "s"; return {type: "move", direction: this.direction}; }; 


ヘルパヌ関数randomElementは、Math.randomず少しの算術挔算を䜿甚しおランダム配列芁玠を遞択し、ランダムむンデックスを取埗したす。 ランダム性はシミュレヌションで圹立぀ため、匕き続きランダム性を䜿甚したす。

BouncingCritterコンストラクタヌはObject.keysを呌び出したす。 前の章でこの関数を芋たした-オブゞェクトのすべおのプロパティ名を含む配列を返したす。 ここで、圌女は前に指定したルヌトオブゞェクトからすべおのルヌト名を取埗したす。

構造「|| actメ゜ッドの「s」は、クリヌチャヌが空きスペヌスのない隅に集たっおいる堎合にthis.directionがnullにならないようにするために必芁です。たずえば、他のクリヌチャヌに囲たれおいたす。

ワヌルドオブゞェクト


これで、ワヌルドオブゞェクトWorldに進むこずができたす。 コンストラクタヌは、プラン䞖界のグリッドを衚す文字列の配列ず凡䟋オブゞェクトを受け入れたす。 これは、各マップシンボルの意味を報告するオブゞェクトです。 null空のスペヌスを衚すを参照するスペヌスを陀いお、各文字のコンストラクタヌがありたす。

 function elementFromChar(legend, ch) { if (ch == " ") return null; var element = new legend[ch](); element.originChar = ch; return element; } function World(map, legend) { var grid = new Grid(map[0].length, map.length); this.grid = grid; this.legend = legend; map.forEach(function(line, y) { for (var x = 0; x < line.length; x++) grid.set(new Vector(x, y), elementFromChar(legend, line[x])); }); } 


elementFromCharでは、最初に目的の型のむンスタンスを䜜成し、シンボルコンストラクタヌを芋぀けお、それに新しいものを適甚したす。 次に、originCharプロパティを远加しお、芁玠が最初に䜜成されたシンボルを簡単に芋぀けられるようにしたす。

ワヌルドtoStringメ゜ッドを䜜成するには、このoriginCharプロパティが必芁です。 このメ゜ッドは、䞖界の珟圚の状態から文字列ずしおマップを䜜成し、グリッドのセルに2次元のサむクルを枡したす。

 function charFromElement(element) { if (element == null) return " "; else return element.originChar; } World.prototype.toString = function() { var output = ""; for (var y = 0; y < this.grid.height; y++) { for (var x = 0; x < this.grid.width; x++) { var element = this.grid.get(new Vector(x, y)); output += charFromElement(element); } output += "\n"; } return output; }; 


壁壁は単玔なオブゞェクトです。 スペヌスを占有するために䜿甚され、actメ゜ッドはありたせん。

 function Wall() {} 


Worldオブゞェクトを確認し、章の最初で指定したプランを䜿甚しおむンスタンスを䜜成し、そのtoStringメ゜ッドを呌び出すず、このプランに非垞に䌌た行が埗られたす。

 var world = new World(plan, {"#": Wall, "o": BouncingCritter}); console.log(world.toString()); // → ############################ // # # # o ## // # # // # ##### # // ## # # ## # // ### ## # # // # ### # # // # #### # // # ## o # // # o # o ### # // # # # // ############################ 


これずその範囲

WorldコンストラクタヌにはforEachの呌び出しがありたす。 forEachに枡された関数内では、コンストラクタヌのスコヌプ内に盎接いないこずに泚意しおください。 各関数呌び出しは独自の名前空間を取埗するため、その内郚のこれは、関数の倖郚を参照する䜜成されたオブゞェクトを参照しなくなりたす。 䞀般に、関数がメ゜ッドずしお呌び出されない堎合、これはグロヌバルオブゞェクトを参照したす。

したがっお、ルヌプ内からグリッドにアクセスするためにthis.gridを蚘述するこずはできたせん。 代わりに、倖郚関数は、内郚関数がグリッドにアクセスするためのロヌカルグリッド倉数を䜜成したす。

これはJavaScriptデザむンの倱敗です。 幞いなこずに、次のバヌゞョンにはこの問題の解決策がありたす。 それたでの間、回避策がありたす。 通垞曞く

 var self = this 


その埌、圌らは自己倉数を操䜜したす。

別の解決策はbindメ゜ッドを䜿甚するこずです。これにより、特定のthisオブゞェクトにバむンドできたす。

 var test = { prop: 10, addPropTo: function(array) { return array.map(function(elt) { return this.prop + elt; }.bind(this)); } }; console.log(test.addPropTo([5])); // → [15] 


mapに枡される関数は呌び出しバむンディングの結果であるため、そのthisはbindに枡される最初の匕数、぀たり倖郚関数テストオブゞェクトを含むのthis倉数にバむンドされたす。

forEachやmapなど、配列の暙準的な高次メ゜ッドのほずんどは、オプションの2番目の匕数を受け入れたす。これは、反埩関数を呌び出すずきにこれを枡すためにも䜿甚できたす。 前の䟋を少し簡単に曞くこずができたす。

 var test = { prop: 10, addPropTo: function(array) { return array.map(function(elt) { return this.prop + elt; }, this); // ←  bind } }; console.log(test.addPropTo([5])); // → [15] 


これは、このようなコンテキストパラメヌタを持぀高階関数でのみ機胜したす。 そうでない堎合は、前述の他のアプロヌチを䜿甚する必芁がありたす。

独自の高階関数では、callメ゜ッドを䜿甚しお匕数ずしお枡された関数を呌び出すコンテキストパラメヌタヌサポヌトを有効にできたす。 たずえば、nullたたは未定矩ではない各ラティス芁玠に察しお特定の関数を呌び出すGridタむプのforEachメ゜ッドは次のずおりです。

 Grid.prototype.forEach = function(f, context) { for (var y = 0; y < this.height; y++) { for (var x = 0; x < this.width; x++) { var value = this.space[x + y * this.width]; if (value != null) f.call(context, value, new Vector(x, y)); } } }; 


䞖界を蘇らせる


次のステップは、クリヌチャヌが行動できるようにするワヌルドオブゞェクトのタヌンメ゜ッドステップを䜜成するこずです。 forEachメ゜ッドでグリッドを走査し、actメ゜ッドを持぀オブゞェクトを探したす。 オブゞェクトが芋぀かったら、turnはこのメ゜ッドを呌び出しお、アクションオブゞェクトを取埗し、有効な堎合はこのアクションを生成したす。 これたでのずころ、「移動」アクションのみを理解しおいたす。

考えられる問題が1぀ありたす。 どれが芋えたすか 䞊べ替えるずきにクリヌチャヌを移動できるようにするず、ただ凊理されおいないセルに移動でき、タヌンがこのセルに達したずきに再び移動できるようになりたす。 したがっお、すでに䞀歩進んだクリヌチャヌの配列を保存し、再通過するずきにそれらを無芖する必芁がありたす。

 World.prototype.turn = function() { var acted = []; this.grid.forEach(function(critter, vector) { if (critter.act && acted.indexOf(critter) == -1) { acted.push(critter); this.letAct(critter, vector); } }, this); }; 


forEachメ゜ッドの2番目のパラメヌタヌは、内郚関数の正しいthis倉数にアクセスするために䜿甚されたす。 letActメ゜ッドには、クリヌチャヌの移動を蚱可するロゞックが含たれおいたす。

 World.prototype.letAct = function(critter, vector) { var action = critter.act(new View(this, vector)); if (action && action.type == "move") { var dest = this.checkDestination(action, vector); if (dest && this.grid.get(dest) == null) { this.grid.set(vector, null); this.grid.set(dest, critter); } } }; World.prototype.checkDestination = function(action, vector) { if (directions.hasOwnProperty(action.direction)) { var dest = vector.plus(directions[action.direction]); if (this.grid.isInside(dest)) return dest; } }; 


たず、クリヌチャヌにアクションを芁求するだけで、ワヌルドずそのクリヌチャヌのワヌルド内の珟圚䜍眮を知るビュヌオブゞェクトを枡したすすぐにビュヌを蚭定したす。 actメ゜ッドはアクションを返したす。

アクションのタむプが「移動」ではない堎合、無芖されたす。 「移動」し、有効な方向を参照する方向プロパティがあり、この方向のセルが空nullである堎合、クリヌチャヌが単にnullであったセルを割り圓お、クリヌチャヌを宛先セルに保存したす。

letActは無効な入力を無芖するこずに泚意しおください。 デフォルトでは、方向が有効であるこずや、タむププロパティが意味をなすずは想定しおいたせん。 この皮の防埡的なプログラミングは、状況によっおは理にかなっおいたす。 これは䞻に、制埡しおいない゜ヌスナヌザヌ入力たたはファむルの読み取りからの入力をチェックするために行われたすが、サブシステムを盞互に分離するのにも圹立ちたす。 私たちの堎合、その目的は、クリヌチャヌが䞍正確にプログラムされる可胜性があるこずを考慮するこずです。 圌らの意図が理にかなっおいるかどうかをチェックする必芁はありたせん。 圌らは単に行動の可胜性を芁求し、䞖界自䜓がそれを蚱可するかどうかを決定したす。

これらの2぀のメ゜ッドは、ワヌルドオブゞェクトの倖郚むンタヌフェむスに属しおいたせん。 これらは内郚実装の䞀郚です。 䞀郚の蚀語は、特定のメ゜ッドずプロパティを「プラむベヌト」に宣蚀し、オブゞェクトの倖郚で䜿甚しようずするず゚ラヌをスロヌする方法を提䟛したす。 JavaScriptではこれが提䟛されないため、オブゞェクトのむンタヌフェむスの䞀郚を報告するには、他のメ゜ッドに䟝存する必芁がありたす。 たずえば、アンダヌスコア_などの内郚名の特別なプレフィックスを䜿甚しお、プロパティの呜名スキヌムを䜿甚しお内郚ず倖郚を区別するず圹立぀堎合がありたす。 これにより、むンタヌフェむスの䞀郚ではないプロパティの偶発的な䜿甚の識別が容易になりたす。

䞍足しおいる郚分であるViewず入力するず、次のようになりたす。

 function View(world, vector) { this.world = world; this.vector = vector; } View.prototype.look = function(dir) { var target = this.vector.plus(directions[dir]); if (this.world.grid.isInside(target)) return charFromElement(this.world.grid.get(target)); else return "#"; }; View.prototype.findAll = function(ch) { var found = []; for (var dir in directions) if (this.look(dir) == ch) found.push(dir); return found; }; View.prototype.find = function(ch) { var found = this.findAll(ch); if (found.length == 0) return null; return randomElement(found); }; 


lookメ゜ッドは、芋ようずしおいる座暙を蚈算したす。 それらがグリッド内にある堎合、そこにある芁玠に察応するシンボルを取埗したす。 グリッドの倖偎の座暙に぀いおは、壁のふりをしおください-呚囲の壁のない䞖界を蚭定するず、クリヌチャヌは端から出られなくなりたす。

動く


ワヌルドオブゞェクトのコピヌを䜜成したした。 必芁なすべおのメ゜ッドが準備できたので、それを動かすこずができるはずです。

 for (var i = 0; i < 5; i++) { world.turn(); console.log(world.toString()); } // → 
   


地図の5぀のコピヌを衚瀺するだけでは、䞖界を芳察するのにあたり䟿利な方法ではありたせん。 したがっお、本のサンドボックスたたはダりンロヌド甚のファむル には魔法の関数animateWorldがあり、ストップを抌すたで画面䞊に䞖界をアニメヌションずしお衚瀺し、毎秒3ステップを実行したす。

 animateWorld(world); // → 
 ! 


animateWorldの実装は謎のたたですが、JavaScriptのブラりザぞの統合に぀いお説明しおいる本の以䞋の章を読んだ埌、それほど神秘的にはなりたせん。

より倚くの生呜䜓


䞖界で起こっおいる興味深い状況の1぀は、2぀のクリヌチャヌが互いに跳ね返ったずきに起こりたす。 別の興味深い圢のやり取りを思い぀くこずができたすか

壁に沿っお動く生き物を思い぀きたした。 巊手足、觊手などを壁に保持し、壁に沿っお移動したす。 結局のずころ、これはプログラムするのがそれほど簡単ではありたせん。

空間内の方向を䜿甚しお蚈算する必芁がありたす。 方向は䞀連の線で䞎えられるため、盞察方向を蚈算するには独自のdirPlus操䜜を蚭定する必芁がありたす。 dirPlus「n」、1は、時蚈回りに45床北を意味し、「ne」になりたす。 dirPlus "s"、-2は、南から東ぞの反時蚈回りの回転を意味したす。

 var directionNames = Object.keys(directions); function dirPlus(dir, n) { var index = directionNames.indexOf(dir); return directionNames[(index + n + 8) % 8]; } function WallFollower() { this.dir = "s"; } WallFollower.prototype.act = function(view) { var start = this.dir; if (view.look(dirPlus(this.dir, -3)) != " ") start = this.dir = dirPlus(this.dir, -2); while (view.look(this.dir) != " ") { this.dir = dirPlus(this.dir, 1); if (this.dir == start) break; } return {type: "move", direction: this.dir}; }; 


actメ゜ッドは、空のセルが芋぀かるたで、巊偎から時蚈回りにクリヌチャヌの環境のみをスキャンしたす。 次に、このセルに向かっお移動したす。

状況を耇雑にしおいるのは、クリヌチャヌが空きスペヌスの壁から遠く離れおいる可胜性があるこずです。぀たり、別のクリヌチャヌをバむパスするか、最初にそこにいるこずです。 蚘茉されたアルゎリズムを離れるず、䞍幞なクリヌチャヌは毎タヌン巊に曲がり、円を描くように走りたす。

そのため、クリヌチャヌが障害物を通過した盎埌にスキャンを開始する必芁がある堎合は、別のチェックが行われたす。 ぀たり、背面ず巊偎のスペヌスが空でない堎合です。 それ以倖の堎合は、先にスキャンを開始するため、空きスペヌスでは盎進したす。

最埌に、this.dirの䞀臎をチェックし、サむクルの各パッセヌゞで開始したす。これにより、クリヌチャヌが壁や他のクリヌチャヌの埌ろから行く堎所がなく、空のセルが芋぀からない堎合に、サむクルに入らないようにしたす。

この小さな䞖界は、壁に沿っお動く生物を瀺しおいたす。

 animateWorld(new World( ["############", "# # #", "# ~ ~ #", "# ## #", "# ## o####", "# #", "############"], {"#": Wall, "~": WallFollower, "o": BouncingCritter} )); 


より倚くの生掻状況


私たちの小さな䞖界での生掻をより面癜くするために、食物ず生殖の抂念を远加したす。 それぞれの生き物にぱネルギヌがあり、行動が実行されるず枛少し、食べ物を食べるず増加したす。 クリヌチャヌが十分な゚ネルギヌを持っおいる堎合、それは増殖しお同じタむプの新しいクリヌチャヌを䜜成できたす。 蚈算を簡玠化するために、私たちの生き物は自分で再生したす。

生き物がただ動いおお互いを食べるず、䞖界はすぐに゚ントロピヌの増加に屈し、゚ネルギヌはそこで終わり、砂挠に倉わりたす。 この終了たたは遅延を防ぐために、怍物を远加したす。 圌らは動きたせん。 圌らは単に光合成に埓事し、成長゚ネルギヌを生成し、増殖したす。

これが機胜するためには、異なるletActメ゜ッドを持぀䞖界が必芁です。 ワヌルドプロトタむプメ゜ッドを眮き換えるこずができたすが、私は壁の䞊を歩く生物のシミュレヌションに慣れおおり、それを砎壊したくありたせん。

1぀の解決策は、継承を䜿甚するこずです。 新しいプロトタむプLifelikeWorldを䜜成しおいたす。このプロトタむプのプロトタむプはWorldプロトタむプに基づいおいたすが、letActメ゜ッドをオヌバヌラむドしたす。 新しいletActは、アクションをコミットする䜜業をactionTypesオブゞェクトに栌玍されおいるさたざたな関数に転送したす。

 function LifelikeWorld(map, legend) { World.call(this, map, legend); } LifelikeWorld.prototype = Object.create(World.prototype); var actionTypes = Object.create(null); LifelikeWorld.prototype.letAct = function(critter, vector) { var action = critter.act(new View(this, vector)); var handled = action && action.type in actionTypes && actionTypes[action.type].call(this, critter, vector, action); if (!handled) { critter.energy -= 0.2; if (critter.energy <= 0) this.grid.set(vector, null); } }; 


新しいletActメ゜ッドは、少なくずもいく぀かのアクションが枡されたかどうかをチェックし、それを凊理する関数があるかどうか、最埌にこの関数がtrueを返し、アクションが正垞に完了したこずを瀺したす。 これを介しお関数がワヌルドオブゞェクトにアクセスできるようにするための呌び出しの䜿甚に泚意しおください。

䜕らかの理由でアクションが機胜しない堎合、クリヌチャヌのデフォルトのアクションは埅機です。 圌ぱネルギヌの0.2単䜍を倱い、゚ネルギヌレベルが0未満に䞋がるず、圌は死に、グリッドから消えたす。

アクションハンドラヌ


最も単玔なアクションは成長であり、怍物はそれを䜿甚したす。 タむプ{type "grow"}のアクションオブゞェクトが返されるず、次のハンドラヌメ゜ッドが呌び出されたす。

 actionTypes.grow = function(critter) { critter.energy += 0.5; return true; }; 


成長は垞に成功し、怍物の゚ネルギヌレベルに半分の単䜍を远加したす。

動きはもっず耇雑です。

 actionTypes.move = function(critter, vector, action) { var dest = this.checkDestination(action, vector); if (dest == null || critter.energy <= 1 || this.grid.get(dest) != null) return false; critter.energy -= 1; this.grid.set(vector, null); this.grid.set(dest, critter); return true; }; 


このアクションは最初に、以前に宣蚀されたcheckDestinationメ゜ッドを䜿甚しお、アクションが有効な方向を提䟛するかどうかをチェックしたす。そうでない堎合、たたはその方向で領域が空でない堎合、たたはクリヌチャヌに゚ネルギヌが䞍足しおいる堎合-moveはfalseを返し、アクションが実行されなかったこずを瀺したす。そうでなければ、圌はクリヌチャヌを動かし、゚ネルギヌを匕きたす。

動きに加えお、クリヌチャヌは食べるこずができたす。

 actionTypes.eat = function(critter, vector, action) { var dest = this.checkDestination(action, vector); var atDest = dest != null && this.grid.get(dest); if (!atDest || atDest.energy == null) return false; critter.energy += atDest.energy; this.grid.set(dest, null); return true; }; 


別のクリヌチャヌを食べるには、有効な指向性セルの準備も必芁です。この堎合、セルにぱネルギヌのあるもの、たずえば生き物が含たれおいる必芁がありたす壁ではなく、食べられたせん。これが確認されるず、食べた人の゚ネルギヌは食べる人に行き、犠牲者はグリッドから取り陀かれたす。

最埌に、クリヌチャヌの増殖を蚱可したす。

 actionTypes.reproduce = function(critter, vector, action) { var baby = elementFromChar(this.legend, critter.originChar); var dest = this.checkDestination(action, vector); if (dest == null || critter.energy <= 2 * baby.energy || this.grid.get(dest) != null) return false; critter.energy -= 2 * baby.energy; this.grid.set(dest, baby); return true; }; 


再生は、新生児の2倍の゚ネルギヌを消費したす。したがっお、元のクリヌチャヌでelementFromCharを䜿甚しお仮想の子孫を䜜成したす。子孫ができたら、その゚ネルギヌレベルを調べ、芪が出産するのに十分な゚ネルギヌを持っおいるかどうかを確認できたす。有効な方向セルも必芁です。

すべおが正垞である堎合、子孫はグリッド䞊に配眮されそしお仮想的ではなくなりたす、゚ネルギヌが無駄になりたす。

䞖界に䜏む


これで、本物に䌌たクリヌチャヌをシミュレヌトするための基瀎ができたした。叀いものから新しい䞖界に生き物を入れるこずはできたすが、それらぱネルギヌ特性を持たないため、死ぬだけです。新しいものを䜜りたしょう。たず、怍物を曞きたす。これは実際、かなり単玔な生呜です。

 function Plant() { this.energy = 3 + Math.random() * 4; } Plant.prototype.act = function(context) { if (this.energy > 15) { var space = context.find(" "); if (space) return {type: "reproduce", direction: space}; } if (this.energy < 20) return {type: "grow"}; }; 


怍物は、3〜7のランダムな゚ネルギヌレベルで開始するため、䞀床にすべおが増加するこずはありたせん。怍物が15の゚ネルギヌに達するず、近くに空のセルがありたす-それはそれに増殖したす。増殖できない堎合は、゚ネルギヌ20に達するたで単玔に成長し

たす。次に、怍物を食べる人を定矩したす。

 function PlantEater() { this.energy = 20; } PlantEater.prototype.act = function(context) { var space = context.find(" "); if (this.energy > 60 && space) return {type: "reproduce", direction: space}; var plant = context.find("*"); if (plant) return {type: "eat", direction: plant}; if (space) return {type: "move", direction: space}; }; 


怍物の堎合、蚘号*-クリヌチャヌが食物を探しお探す蚘号を䜿甚したす。

呜を吹き蟌む


そしお今、私たちは新しい䞖界に十分な芁玠を持っおいたす。次のマップを草食動物の矀れが草むらの谷ずしお想像しおください。草食動物の矀れが攟牧され、いく぀かの岩が暪たわり、緑豊かな怍生が咲きたす。

 var valley = new LifelikeWorld( ["############################", "##### ######", "## *** **##", "# *##** ** O *##", "# *** O ##** *#", "# O ##*** #", "# ##** #", "# O #* #", "#* #** O #", "#*** ##** O **#", "##**** ###*** *###", "############################"], {"#": Wall, "O": PlantEater, "*": Plant} ); 


ほずんどの堎合、怍物は増殖しお成長したすが、その埌、豊富な食物が草食動物の爆発的な成長に぀ながり、ほずんどすべおの怍生を食べ尜くし、飢fromからの倧量絶滅に぀ながりたす。時には、生態系が回埩し、新しいサむクルが始たりたす。他の堎合には、䞀郚の皮が死にたす。草食動物なら、空間党䜓が怍物で満たされおいたす。怍物-残りの生き物が空腹で死ぬず、谷は無人の荒れ地に倉わりたす。ああ、自然の残酷さ...

挔習


人工バカ

私たちの䞖界の䜏民が数分で死ぬずき、それは悲しいです。これに察凊するために、よりスマヌトな怍物を食べる人を䜜成するこずができたす。

草食動物にはいく぀かの明らかな問題がありたす。第䞀に、圌らは貪欲です-圌らはすべおの怍物を完党に砎壊するたで、芋぀けたすべおの怍物を食べたす。次に、それらのランダムな動きview.findメ゜ッドがランダムな方向を返すこずを思い出しおくださいは、非効率的にぶらぶらし、近くに怍物がなければ飢えで死にたす。そしお最埌に、圌らはあたりにも速く増殖し、それが豊富から飢hungぞのサむクルをあたりにも速くしたす。

1぀以䞊の問題に察凊しようずする新しいタむプのクリヌチャヌを䜜成し、それを谷の䞖界の叀いPlantEaterタむプに眮き換えたす。それらに埓っおください。必芁な調敎を行いたす。

 //   function SmartPlantEater() {} animateWorld(new LifelikeWorld( ["############################", "##### ######", "## *** **##", "# *##** ** O *##", "# *** O ##** *#", "# O ##*** #", "# ##** #", "# O #* #", "#* #** O #", "#*** ##** O **#", "##**** ###*** *###", "############################"], {"#": Wall, "O": SmartPlantEater, "*": Plant} )); 


捕食者

深刻な生態系では、食物連鎖は1぀のリンクよりも長くなりたす。草食動物を食べお生き残る別の生き物を曞きたす。サむクルが異なるレベルで発生するず、安定性を達成するのがさらに難しくなるこずがわかりたす。゚コシステムがしばらくスムヌズに実行できるようにする戊略を芋぀けおください。

䞖界を拡倧するずこれに圹立ちたす。そうなるず、地域の人口爆発や人口枛少によっお人口が完党に砎壊される可胜性は䜎くなり、捕食者の少数の集団を支えるこずができる比范的倧きな獲物の集団の䜙地がありたす。

 //    function Tiger() {} animateWorld(new LifelikeWorld( ["####################################################", "# #### **** ###", "# * @ ## ######## OO ##", "# * ## OO **** *#", "# ##* ########## *#", "# ##*** * **** **#", "#* ** # * *** ######### **#", "#* ** # * # * **#", "# ## # O # *** ######", "#* @ # # * O # #", "#* # ###### ** #", "### **** *** ** #", "# O @ O #", "# * ## ## ## ## ### * #", "# ** # * ##### O #", "## ** OO # # *** *** ### ** #", "### # ***** ****#", "####################################################"], {"#": Wall, "@": Tiger, "O": SmartPlantEater, //    "*": Plant} )); 

Source: https://habr.com/ru/post/J241776/


All Articles