-まず、主なことを理解する必要があります...
-主なものは何ですか?
-スプーンはありません!
マトリックス
前にも繰り返し言ったよう
に 、
Zillions of Gamesで特定のものを実装することは単に不可能です。 ただし、できないが、本当にしたい場合は、それでも可能です。 このようにどこまで行けますか?
3.すべてはデマです
すでにこれに遭遇しています。 ZoGは動きを決定することはできません。ボードからピースを取り除くことに限定されますか? 問題ありません-ピースをボードに
捨ててすぐに取り除くムーブを作成します(その結果、ターゲットフィールドにあるピースは自動的に削除されます)。 したがって、「ボタン」を作成して、ZoGのインターフェース機能を拡張することができます(実際、これらはボタンではなく、ボード上の単なる特別な部分です)。 ZoGはボードの寸法を5つの寸法に制限していますか? どちらでも構いません!
不足しているアイテムを作成し、それらを手動で結びます。(define 6D (image "images\6DChess\6DChess.bmp") (grid (start-rectangle 34 7 76 53) (dimensions
怖い? しかし、それは
動作します!
少しだまされて、六角形のボード
を定義する
ことが
できます:
これは同じ2次元グリッドであり、行は相互にシフトされています (define Board-Definitions (image "Images\6Chess.bmp") (grid (start-rectangle 21 -79 69 -31) (dimensions ("a/b/c/d/e/f/g/h/i" (38 22))
ボードの一部を、それらのピースと一緒に移動
できます!
とても簡単です。
実際、2つのボードがあります (board (image "Images\PlatformChess\Void8x8.bmp") (grid (start-rectangle 10 10 48 48) (dimensions ("a/b/c/d/e/f/g/h" (49 0))
従来の8x8ボードは、4x4ボードと組み合わされ、それに沿って「ボードの一部」、つまりプラットフォームが移動します。 追加の
上下方向は、フィギュアが「足の下の土」の存在を制御するのに役立ちます。 最も困難なことは、プラットフォームと一緒にフィギュアの同期移動を保証することです。
カスケードおよびオペレーターへの救助 (define wplatform-move (mark $1 (verify empty?) to back up (if (and empty? (empty? e) (empty? s) (empty? se)) add else back up (if not-empty? cascade from $1 (if (and (in-zone? promotion-zone $1) (piece? Pawn (opposite $1)) (friend? (opposite $1))) $1 (change-type Queen) to else $1 to)) back up e (if not-empty? cascade from $1 (if (and (in-zone? promotion-zone $1) (piece? Pawn (opposite $1)) (friend? (opposite $1))) $1 (change-type Queen) to else $1 to)) back up s (if not-empty? cascade from $1 (if (and (in-zone? enemy-promotion-zone $1) (piece? Pawn (opposite $1)) (enemy? (opposite $1))) $1 (change-type Queen) to else $1 to)) back up se (if not-empty? cascade from $1 (if (and (in-zone? enemy-promotion-zone $1) (piece? Pawn (opposite $1)) (enemy? (opposite $1))) $1 (change-type Queen) to else $1 to)) add ) ) )
これはZoGの機能の限界だと思うかもしれませんが、これを見てください:
ここには明らかに何か問題があります。 形状が重なっています! ZoGはとても有能ですか? もちろん違います! しかし、だれが数字は全部でなければならないと言ったのですか? このゲームでは、各「ピース」は4つのピースで構成されており、ボードはもちろん3次元です(ただし、「トッププラン」のみが表示されます)。 実装は素晴らしいです。 紛らわしい唯一のことは、図のいずれかを選択するには、その左上隅を指定する必要があることです(必要に応じて修正できます)。
さらに進むことができます!
この本を初めて見た瞬間から、Margoのボードを作りたいという強い思いがありました。 私は現実主義者なので、AI ZoGを正しく動作させることはほとんどできないことを認識しています。
Goを2次元のボードにクラシックに実装する場合でも、ZoGは平均的な初心者レベルでプレイするための
エンジンを必要としていました。 ゾンビ、ブリッジ、仮想グループを備えたMargoでは、さらに困難です。 本格的なゲームは
Axiomで実装できますが、AI開発には別の話があります。 まず、ゲームのルールを管理し、2人のゲームをサポートし、スケッチの分析をサポートするボードには非常に満足しています。 ボードを定義することから始めます。
ボード ''マーゴ '' (board (image "../images/margo/board.bmp") (grid (start-rectangle 30 30 59 59) (dimensions ("a/b/c/d/e/f/g/h/i/j/k/l/m/n" (30 0))
麻雀ボードの定義に基づいて、グラフィックリソースとボードのサイズを変更しました(7x7ボードを取得するには、7xレイヤーをサポートする14x14タイルの
グリッドを定義する必要があります)。 ここでは、ゲームゾーンの定義に注意する価値があります。 最初に、「トッププラン」、つまり表示されるすべてのタイルが配置される
平面ゾーンを定義する必要があります。 このゾーンでのみ新しい数字を追加することが可能であり、それらの削除はそれから始めるべきです。 さらに、空白のボードにピースを作成することはどこでもできません。
空の計画ゾーンは、左上のタイルの可能な位置を決定します。
「
im 」ゾーンの定義に関しては、これは面白いトリックであり、特定のプレーヤーのフィギュアの所有権(親しみや敵意だけでなく)をさらに判別できます。 このマクロは、現在の位置にあるタイルに関する完全な情報を取得するために使用されます(残念ながら、ZRFにこれ以上のわかりやすい方法で同様の機能を実装することは不可能です)。
タイル情報を取得する (define get-piece (set-flag is-piece? false) (set-flag is-black? false) (set-flag is-left? false) (set-flag is-top? false) (if not-empty? (set-flag is-piece? true) (if (and enemy? (in-zone? im a1I)) (set-flag is-black? true) ) (if (and friend? (in-zone? im a2I)) (set-flag is-black? true) ) (if (or (piece? tnw) (piece? tne)) (set-flag is-top? true) ) (if (or (piece? tnw) (piece? tsw)) (set-flag is-left? true) ) ) )
空のボードにピース(4つのタイル)を配置するタスクは簡単に解決できます。 唯一の技術的な難しさは、
別の特別なピース(タイルではない)を破棄することから移動が開始されることです。これは、フィールドにリセットできる唯一のピースである
ためです。 リセットポイントでは、対応するタイルに置き換える必要があります(これは、前の記事のHuffing Checkerで行ったものとほぼ同じです)。 残りのタイルは、隣接するフィールドで単純に作成されます(移動は任意のタイルで開始できるため、4つの可能な移動を決定する必要があります)。
空のボードに移動する (define pre-check-3 (verify (on-board? $1)) (verify (on-board? $2)) (verify (in-zone? $3)) ) (define pre-check-4 (verify (on-board? $1)) (verify (on-board? $2)) (verify (in-zone? $3 $4)) ) (define check-e (verify empty?) (verify (empty? $1)) (verify (empty? $2)) (verify (empty? $3)) ) (define post-action (create t-$1) (create t-$2 $2) (create t-$3 $4) (create t-$5 $6) add ) (define de-nw ( (pre-check-3 es empty-plane) (check-e es se) (post-action nw se ne e sw s) )) (define de-ne ( (pre-check-4 ws empty-plane w) (check-e ws sw) (post-action ne sw nw w se s) )) (define de-se ( (pre-check-4 wn empty-plane nw) (check-e nw nw) (post-action se nw ne n sw w) )) (define de-sw ( (pre-check-4 en empty-plane n) (check-e en ne) (post-action sw ne nw n se e) )) (define tile (name $1$2) (image G "../images/margo/w$1$2.bmp" R "../images/margo/b$1$2.bmp") (attribute is-ko? false) ) (piece (tile t nw) ) (piece (tile t ne) ) (piece (tile t sw) ) (piece (tile t se) ) (piece (name M) (image R "../images/margo/m.bmp" B "../images/margo/m.bmp") (drops (de-nw) (de-ne) (de-se) (de-sw) ) )
「ピラミッド」を構築する動きはより複雑です。 新しい図が配置される4つのタイルすべてに必要なタイプがあり、それらの下の深さが同じであることを確認する必要があります。 さらに、ボード上の上部(可視)レイヤーの図を配置する前に、以前に配置したタイルをより深いレイヤーに転送する必要があります。 最初は、上から下へレイヤー
IIから
VIIを「スタック」として使用する、完全に成功したソリューションではありませんでした。
ピラミッド (define check-p (verify (piece? t-$1)) (verify (piece? t-$2 $3)) (verify (piece? t-$4 $5)) (verify (piece? t-$6 $7)) ) (define check-d mark u (while not-empty? (verify (not-empty? $1)) (verify (not-empty? $2)) (verify (not-empty? $1$2)) u ) (verify (empty? $1)) (verify (empty? $2)) (verify (empty? $1$2)) back ) (define dp-nw ( (pre-check-3 es plane) (check-p se sw e nw se ne s) (check-d se) (pre-action se) (post-action nw se ne e sw s) )) (define dp-ne ( (pre-check-4 ws plane w) (check-p sw se w nw s ne sw) (check-d sw) (pre-action sw) (post-action ne sw nw w se s) )) (define dp-se ( (pre-check-4 wn plane nw) (check-p nw sw n ne w se nw) (check-d nw) (pre-action nw) (post-action se nw ne n sw w) )) (define dp-sw ( (pre-check-4 en plane n) (check-p ne nw e se n sw ne) (check-d ne) (pre-action ne) (post-action sw ne nw n se e) )) (piece (name M) (image R "../images/margo/m.bmp" B "../images/margo/m.bmp") (drops (de-nw) (dp-nw) (de-ne) (dp-ne) (de-se) (dp-se) (de-sw) (dp-sw) ) )
その後、同じ図形のすべてのタイル(表示されているものを除く)を1つのレイヤーに保持する方がはるかに便利であることが明らかになりました。 スタックを「
裏返す 」必要がありました(このため、最初のレイヤーと7番目のレイヤーを接続する追加の方向を作成する必要がありました)。 次のステップは削除でした。 Goでの「死」(およびMargoでの死)に関連して形状を削除するアルゴリズムは非常に複雑です。 単純なものから始めることにしました-単純なマウスクリックで形状を削除します。 もちろん、構造全体がバラバラにならないように、同様に、リモートに「静止」しているすべての図形を(直接または間接的に)削除する必要があります。 マークされたタイル自体を削除することは非常に明白です。
タイルの一括削除 (define clean-piece (set-flag is-piece? false) d (while empty? d) (while (and not-empty? (position-flag? is-dead?)) capture d ) (if not-empty? (get-piece) capture ) (while (not-in-zone? plane) u) (if (flag? is-piece?) (add-piece) else capture ) ) (define clean-pieces mark a0 (while (on-board? next) next (if (and not-empty? (position-flag? is-dead?)) (if (empty? b) capture else (clean-piece) ) ) ) back )
メインの
クリーンピースループでは、すべての最上位タイルがスキャンされます。 それらのいずれかが削除対象としてマークされている場合、タイルはその下の下位レイヤーでチェックされます。 削除のマークが付いているものはすべて削除され、最初のタグなしタイルは最初のレイヤーに移動します。 位置フラグはマーキングに使用され、ブール値を任意の位置に関連付けることができます。 削除するタイルをマークする方法を理解することは残っています。
削除するタイルをマークする (define check-top (verify (on-board? $1)) (if (and (not-empty? $1) (not-position-flag? is-dead? $1) (not-position-flag? is-current? $1)) $1 (set-position-flag is-current? true) (if (not-piece? t$3) d (while (and empty? (on-board? d)) d) (verify not-empty?) (if (and not-empty? (not-position-flag? is-dead?) (not-position-flag? is-current?)) (set-position-flag is-current? true) ) (while (not-in-zone? plane) u) ) $2 ) ) (define check-deep (verify (on-board? $1)) (if (and (not-empty? $1) (not-position-flag? is-dead? $1) (not-position-flag? is-current? $1)) $1 (set-position-flag is-current? true) (verify (on-board? u)) (if (and (not-empty? u) (not-position-flag? is-dead? u) (not-position-flag? is-current? u)) u (set-position-flag is-current? true) d ) $2 ) ) (define proceed-current (set-position-flag is-dead? true) (set-position-flag is-current? false) (get-piece) (if (in-zone? plane) (if (flag? is-left?) (if (flag? is-top?) (check-top ew ne) (check-top sn sw) (check-top se nw se) else (check-top ew se) (check-top ns nw) (check-top ne sw ne) ) else (if (flag? is-top?) (check-top we nw) (check-top sn se) (check-top sw ne sw) else (check-top we sw) (check-top ns ne) (check-top nw se nw) ) ) else (if (flag? is-left?) (if (flag? is-top?) (check-deep ew) (check-deep sn) (check-deep se nw) else (check-deep ew) (check-deep ns) (check-deep ne sw) ) else (if (flag? is-top?) (check-deep we) (check-deep sn) (check-deep sw ne) else (check-deep we) (check-deep ns) (check-deep nw se) ) ) ) ) (define proceed-all (set-flag is-done? false) (while (not-flag? is-done?) (set-flag is-done? true) mark a0 (while (on-board? next-all) next-all (if (position-flag? is-current?) (set-flag is-done? false) (proceed-current) ) ) back ) ) (define dp-clear ( (verify not-empty?) (set-position-flag is-current? true) (proceed-all) (clean-pieces) add )) ... (piece (name M) (image G "../images/margo/m.bmp" R "../images/margo/m.bmp") (drops (de-nw) (dp-nw) (de-ne) (dp-ne) (de-se) (dp-se) (de-sw) (dp-sw) (dp-clear) ) )
ここでは、削除は
常に最初のレイヤーにある可視タイルから始まることを理解することが重要です。
dp-clearで初期マークを設定し
たら 、まだ削除されていない新しいタイルが見つかるまで、
proceed-currentを実行し
ます 。 この手順自体は、隣接するタイルへのフラグの「配布」を実行し、アクションが上位レイヤーで実行されるか深さで実行されるかによって異なります。
最初のレイヤーでは、隣接するタイルのタイプが制御され、予想と一致しない場合(これは、図が上にあるものと重なることを意味します)、重なり合う図のタイルをマークすることに加えて、フラグは深さ方向に広がります。 より深いレイヤーでは、形状タイルは常に同じレイヤーに配置されます。 フラグを隣接するタイルに拡張し、その上に何か他のものがある場合は、それも渡します。 その結果、すべてが期待どおりに機能しますが、画像自体は次のようになります。
さて...これらはタイルです。 次の図の色を考慮するには、グラフィカルエディターで作業し、各タイルにさらに2つのタイプを追加し、コードを少し複雑にする必要があります。 最終的に、すべて一緒に
このようになります。
もちろん、この空白を本格的なMargoゲームに変えるには、まだやることがたくさんあります。 おそらく、私はまだAxiomですべてをやり直す必要がありますが、これ自体は物語全体です。 Axiomにはシェイプ、ゲームゾーン、および位置フラグの属性はありませんが、開発(AIを含む)の点では、その機能はZRFよりも非常に優れています。 さらに、ForthScriptコードは少なくともデバッグできます。 私がすべての困難に対処する場合(そしてそうする場合)、これは別の記事に値するでしょう。今日は、ZRFの助けを借りて、本当に驚くべきこと
ができることを示したかっただけ
です 。 少しの工夫を見せて、あなたの手で働くことを恐れないでください。