障害追跡



なんて遅い国! -女王は言った。 -さて、ここで、

あなたは知っている、あなたはただ滞在するだけ速く走らなければならない

同じ場所に! 別の場所に行きたいなら

少なくとも2倍の速度で実行する必要があります!



ルイス・キャロル「 アリス・スルー・ザ・ルッキング・グラス



今日は、2年弱前に出会った、過小評価されている素晴らしいゲームについてお話したいと思います。 ある意味で、 Dmitry Skiryukと知り合いになったのは、このゲームとUrの おかげです 。 当時、私はボードゲームに興味を持ち始めていました。 私の知識は乏しく、多くの点で素朴でした。 チェイスのようなゲームは文字通り私に広大な世界を開いた。 今でも、このゲームでの作業は、大部分が探偵小説に似ています。 この点で、ゲーム「 チェイス 」は、その名前と類似点の両方を、有名なアメリカの作家の仮名と完全に正当化しました。



ゲームはトムクルシェフスキーによって開発され、1986年にTSRによって開始されました。 特別なボードに加えて、各プレイヤーは10ヘクスのサイコロを持っていますが、それにもかかわらず、このゲームは偶然のゲームではありません。 キューブは、進行の順序を決定するために一度だけスローされ、将来的には数字としてのみ使用されます。 上側のポイントの数は、キューブを移動できるステップの数を示しています。 したがって、1つのポイントを持つキューブは、2つのポイント(直線上の2つのライン、3つのライン、3つのラインなど)を使用して、6つの方向のいずれかに隣接するフィールドに移動できます キューブは、指定されたステップ数だけ、正確に移動する必要があります。 移動の過程で、立方体は反対側を回転しません(上面の点の数は変わりません)。 初期配置は次のとおりです。









ルールの詳細をご覧ください。
各プレーヤーの上面の合計ポイント数は25です。プレーヤーは、ゲームの終了までこの量を維持する必要があります。 プレイヤーはターンを取り、そのうちの1人が1人(または2人、これも可能)の場合、対戦相手はゲームから除去されたポイントの合計を最小ポイント数でサイコロに追加する必要があります(ゲームの開始時、これはユニットの1つです)。 その後、すべてのポイントが分散されない場合、残りはさらに分散され、常に最小のポイントを持つキューブから開始されます。 サイコロの残りが5個未満のプレーヤーは、ボードに残っているサイコロに必要なポイント数を分配できないため、負けとなります。









ボードの境界は、ピースの動きを妨げません。 ボードの左右の境界線は互いに「接着」されており、図の上下の境界線からはね返って跳ね返ります。 もちろん、これは数字が妨げられずに動くことを意味しません。 人物はお互いに「飛び越える」ことはできず、「 商工会議所 」の中央フィールドも同様です。 対戦相手の駒をキャプチャするには、その駒が完全な数のステップを直線で完了することによって、その上に「立つ」必要があります(チェスキャプチャ)。 移動は、その色の数字で終了する場合があります。 この場合、「 バンピング 」が発生します。ターゲットフィールドに表示される図形は、1ステップずつ移動し、移動方向を継続します(ボードとリバウンドの結合を考慮に入れて)。 次のフィールドも彼のフィギュアで占められている場合、「 バンピング 」は最初の空のフィールドまたは敵のフィギュアで占められているフィールドまでさらに広がります(敵のフィギュアは取り除かれます)。 このような動きを不可能にする障害は1つだけです。 バンピングを使用して中央のセルに図形を「押し込む」ことは禁止されています。



最初の位置から、各プレーヤーは自分のすべてのピースを周期的にシフトし、いずれかのユニットを2の方向に移動できることに気付くことができます。 同様の動きが規則で許可されています。 また、隣接するフィールドにある同じ色の数字の間でポイントの「交換」を許可しました。 したがって、5と2のペアは、4と3、または1と6に変わる可能性があります。 調査されていない動きは1種類のみです。 ボードの中央のフィールド( Chamber )を通過できるピースはありませんが、このボードでの動きを終了できます。 これが発生した場合、図はポイントの総数を維持しながら2つに「分割」されます。 数字は常に分割され、受信した数字の1つのポイントが他の数字のポイントを1以下超えないようにします。各プレイヤーの数字の合計数は10を超えることはできません(この場合、ゲームの開始時に、各プレイヤーは1つのダイス予備)。









断片の「拡大」の方向は、矢印の先端に似ています。 多数のポイント(存在する場合)を持つキューブは常に左に移動します。 2つの特殊なケースでは、「分割」は不可能です。 まず、上記で述べたように、1色の立方体の数は10を超えることはできません。さらに、1ポイントで立方体を分割できないことは明らかです。 どちらの場合も、 部屋に入る立方体はそのまま左方向に出ます。 商工会議所を出た各フィギュアは、自分のフィギュアを叩くか、相手のフィギュアを手に入れることでバンピングを開始できます(この方法でのみ、一度に2つの敵の駒を手に入れることができます)。



Tom KruszewskiとTSRは、潜在的な視聴者の能力を大きく過大評価していると言わざるを得ません。 大衆消費者にとって、ゲームは複雑すぎました(チェスもそれほど複雑ではありませんが、誰もが慣れています)。 製造業者は生産を停止しており、現在、Chaseはさまざまな見本市、オークション、販売で手でのみ購入できます。 それでも、このゲームは20世紀の最高のゲームの1つと考えられています。



簡単な仕事



ゲームはボードから始まり、チェイスのボードは...独特です。 以前は、六角形のボードでゲームを作成する必要はありませんでした。これが最初の(非常に小さな)障害でした。 これは興味深い点であり、私はそれについてあなたにもっと伝えたいです。 ZRFでゲーミングボードを記述するためのメカニズムは十分に検討されており、飛行機に表示され、ゲーム中に変化しない限り、ほぼすべてのボードを実装できます。



これは見た目です
(board (image "../Images/Chase/board.bmp") (grid (start-rectangle 48 32 108 82) (dimensions ("a/b/c/d/e/f/g/h/i/j/k/l/m" (60 0)) ("1/2/3/4/5/6/7/8/9" (-30 52)) ) (directions (se 1 1) (w 1 0) (sw 0 1) (nw -1 -1) (e -1 0) (ne 0 -1)) ) (kill-positions j1 k1 l1 m1 j2 k2 l2 m2 a3 k3 l3 m3 a4 k4 l4 m4 a5 b5 l5 m5 a6 b6 l6 m6 a7 b7 c7 m7 a8 b8 c8 m8 a9 b9 c9 d9 ) )
      
      





モデルの詳細が視覚化の問題と混ざることは主張しませんが、一方を他方から分離する必要があるまで(たとえば、ボードをアイソメではなく「正直な」3Dで表示するまで)、このアプローチは非常にうまく機能します。 この説明をさらに詳しく考えてみましょう。



  • 説明の不可欠な部分は、ボードの画像を含むファイルです。 図形の幾何学的なサイズと位置はすべてそれに添付されます(このため、 倉庫番の実装の分布のほとんどは、さまざまな形状とサイズの黒い長方形です)。 ボードの画像をBMP形式( ZoGはこの形式のみを理解)で含むファイルは、キーワードimageによって決定されます 。 ここでは、複数のファイルを一度に(スキン間の切り替えを可能にするために)定義できますが、幾何学的な比率は同じです。
  • gridキーワードを使用すると、位置のn次元配列を記述できます。 ほとんどの場合、これは使い慣れた2次元のボードですが、異なる次元(最大5つ)のボードを定義することもできます。 個々の位置に一意の名前が付けられている場合、ボードは複数のグリッドで構成できます。 「 Quantum Tic-Tac-Toe 」で行われる方法と同様に、強い欲求があれば、 グリッドを別のグリッドの上に配置することさえできます。
  • 「セル」のサイズとグリッドの位置は、 start-rectangleキーワードによって決定されます。 2組の整数は、最初(左上)のセルの左上隅と右下隅の画面座標(x、y)を指定します。
  • 以下は「次元」の説明です。 各説明には、2つの整数だけでなく、名前の文字列(位置の名前はデカルト積によって結合されます)が含まれます。 これらの数字には、六角形や等尺性のボードを記述することができる「魔法」があります。 これは、次のグリッドセルをシフトするシフトにすぎません。 通常(2次元ボードの場合)、1つの次元では、セルはxのセル幅だけシフトされ、もう1つの次元では-yのセルの高さだけシフトされますが、これらのセルをxの幅の半分だけシフトすると、六角形のボードの優れたベースを取得できます。
  • グリッドの 「マジック」の2番目のコンポーネントは方向です。 ボードは位置だけでなく、ボード間の通信(名前付きおよび一方向)でもあります。 もちろん、各接続の名前と位置のペアを指定することによって、各接続を個別に決定する手間はありませんが、大きなボードを定義する場合、このプロセスは面白くありません。 Directionsキーワードを使用すると、位置名ではなく、グリッド内のルートを操作できます。
  • 必要な形状のボードを取得するには、より大きなサイズの「長方形」ボードを使用し、次にセルをセルの半分だけシフトします。 その結果、ボードから「切り離す」必要がある「余分な」ポジションがあります。 kill-positionsキーワードを使用すると、以前に定義された位置名を無効として宣言できます。 もちろん、削除された位置では、対応する接続​​も切断されます。




gridキーワードを使用すると、「標準」ボードを記述する際の手作業の量を大幅に削減できますが、このアプローチには特定の欠点がないわけではありません。 第1に、整数座標とオフセットのみで動作し、選択した幾何学的寸法に合わせてボードの画像が描画されていない場合、ボードのすべての位置の位置を完全に揃えることは困難です。 位置の個々の説明は簡潔ではありませんが、互いに独立して位置を調整できます。 同時に、それは単純な殺人量の手作業を必要とします(行われたすべてのタイプミスを修正する必要性を考慮して)。 このプロセスを簡単にするために、「大まかな」説明にグリッドを使用します。その後、小さなスクリプトを使用して位置の個別の説明を取得します



スクリプト
 my @grid; my %kp; my $sx, $sy, $dx, $dy; my $dm = 0; while (<>) { if (/\(start-rectangle\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\)/) { $sx = $1; $sy = $2; $dx = $3 - $1; $dy = $4 - $2; } if (/\(\"([^\"]+)\"\s+\((-?\d+)\s+(-?\d+)\)\)/) { my @a = split(/\//, $1); $grid[$dm]->{ix} = \@a; $grid[$dm]->{x} = $2; $grid[$dm]->{y} = $3; $dm++; } if (/\(kill-positions/) { $fl = 1; } if ($fl) { if (/\s(([a-z0-9]{1,2}\s+)+)/i) { my @a = split(/\s+/, $1); foreach my $p (@a) { $kp{$p} = 1; } } if (/\)/) { $fl = 0; } } } sub try { my ($ix, $pos, $x, $y) = @_; if ($ix < $dm) { my $i = 0; foreach my $p (@{$grid[$ix]->{ix}}) { try($ix + 1, $pos . $p, $x + $i * $grid[$ix]->{x}, $y + $i * $grid[$ix]->{y}); $i++; } } else { if (!$kp{$pos}) { my $a = $sx + $x; my $b = $sy + $y; my $c = $a + $dx; my $d = $b + $dy; print " "; printf "($pos %3d %3d %3d %3d)\n", $a, $b, $c, $d; } } } try(0, '', 0, 0);
      
      







結果
  (positions (a1 48 32 108 82) (a2 18 84 78 134) (b1 108 32 168 82) (b2 78 84 138 134) (b3 48 136 108 186) (b4 18 188 78 238) (c1 168 32 228 82) (c2 138 84 198 134) (c3 108 136 168 186) (c4 78 188 138 238) (c5 48 240 108 290) (c6 18 292 78 342) (d1 228 32 288 82) (d2 198 84 258 134) (d3 168 136 228 186) (d4 138 188 198 238) (d5 108 240 168 290) (d6 78 292 138 342) (d7 48 344 108 394) (d8 18 396 78 446) (e1 288 32 348 82) (e2 258 84 318 134) (e3 228 136 288 186) (e4 198 188 258 238) (e5 168 240 228 290) (e6 138 292 198 342) (e7 108 344 168 394) (e8 78 396 138 446) (e9 48 448 108 498) (f1 348 32 408 82) (f2 318 84 378 134) (f3 288 136 348 186) (f4 258 188 318 238) (f5 228 240 288 290) (f6 198 292 258 342) (f7 168 344 228 394) (f8 138 396 198 446) (f9 108 448 168 498) (g1 408 32 468 82) (g2 378 84 438 134) (g3 348 136 408 186) (g4 318 188 378 238) (g5 288 240 348 290) (g6 258 292 318 342) (g7 228 344 288 394) (g8 198 396 258 446) (g9 168 448 228 498) (h1 468 32 528 82) (h2 438 84 498 134) (h3 408 136 468 186) (h4 378 188 438 238) (h5 348 240 408 290) (h6 318 292 378 342) (h7 288 344 348 394) (h8 258 396 318 446) (h9 228 448 288 498) (i1 528 32 588 82) (i2 498 84 558 134) (i3 468 136 528 186) (i4 438 188 498 238) (i5 408 240 468 290) (i6 378 292 438 342) (i7 348 344 408 394) (i8 318 396 378 446) (i9 288 448 348 498) (j3 528 136 588 186) (j4 498 188 558 238) (j5 468 240 528 290) (j6 438 292 498 342) (j7 408 344 468 394) (j8 378 396 438 446) (j9 348 448 408 498) (k5 528 240 588 290) (k6 498 292 558 342) (k7 468 344 528 394) (k8 438 396 498 446) (k9 408 448 468 498) (l7 528 344 588 394) (l8 498 396 558 446) (l9 468 448 528 498) (m9 528 448 588 498) )
      
      







これは戦いの半分に過ぎません! ボードの位置名は、一般に受け入れられている表記法に合わせて修正する必要があります。 さらに、エッジの周りでボードを「ループ」することを忘れずに、位置のペアを方向に接続する必要があります。 その結果、かなりの量の手作業が必要になりましたが、このビジネス用のスクリプトは作成しませんでした(おそらくそれだけの価値はありましたが)。



理性の眠り



ずっと前にチェイスに会ったが、最近までプレイできなかった。 これには非常に豪華なボードが必要です。 ある程度のスキルがあれば、将gi盤(9x9)でプレイできますが、私も持っていませんでした。 通常のチェス盤(8x8)は、このゲームにはまったく不適切です。 最後のZilantkonでチェイスのボードを獲得することができましたが、サイコロは含まれていません。 私は買収を遠方の棚に投げましたが、事件が介入しなければ失敗したでしょう。



事故は偶然ではありません
私の娘は、私たちが長い間友人であった家族と一緒に誕生日に招待されました。 贈り物として、 ボードゲームが選ばれました。数人の大人が子供のカフェに3時間ほど座っていなければならなかったので、彼ら(そして私)も何かで占領する必要がありました。 可能な選択肢として、 別のゲームが提案されましたが、私はより抽象的なゲームを好むため、私も何かを持って行くことにしました。 当初、私はUrについて考えましたが、私が持っていたキットでは、半円形の棒の形で作られた彼のD2「骨」( Senetにより典型的)はかなり不快で、投げの間に多くのノイズを出し、他の人と干渉する可能性がありました。



その時、チェイスを思い出しました。 彼のセットに20個のサイコロを補充する必要がありましたが、私はまだボードゲームストア(ギフト用)に向かっていたので、これは(当時のように思えた)問題ではありませんでした。 このサイトで、私は素晴らしい半透明のキューブ(それぞれ70ルーブル)を見ましたが、人生は調整されました。 ストアでは、私が面倒を見ていたキューブが1つのコピーにしか含まれていなかったことが判明しました。 カザンはモスクワではありません。私は予算オプションに満足し、ico、dode、および他の面体の売り手から提供されたプレーサーから必要なキューブを収集する必要がありました。 赤または緑のキットを組み立てることはできませんでしたが、青と白(大丈夫、大丈夫、 わずかに黄色がかった)のキューブが用意されていました。



もちろん、私はルールを誤って解釈しました(記憶について話しました)。 私のプレゼンテーションでは、「レプリケーター」の出口にある「断片」の飛行経路は矢印ではなく、ラテン文字「 Y 」に似ていました。 どうやら、特定の役割は、素粒子の崩壊スキームとの類似性によって果たされた。 「フラグメント」は、ルールの元のバージョンのように1つのセルを移動しませんでしたが、「額面」に従って移動しました。 さらに、このような移動はブロックするのがはるかに簡単でした。 障害物(破片の邪魔になったり、ボード上に10個のピース​​があったりするなど)は、移動できないと解釈されました。 ルールの元のバージョンでは、入力する途中で数字を設定することによってのみ「 商工会議所 」をブロックできます。



破損した電話 」の別のリンクは、 ドミトリー自身でした。 チェイスの説明の中で、彼は、捕獲を行った人物が( チェッカーとの類推によって)再奪取する権利を持っていると述べた。 元のソースにはこれについての言葉はありませんでした(親愛なるゲストは彼に通知することに失敗しませんでした)が、その瞬間、私はそれに注意を払いませんでした。 私は言わなければならない、「チェイス」と「チェッカー」を交差させるというアイデアはすでに多くの疑問を提起した。 リトレース規則をバンピングに拡張する必要がありますか? 図を分割して得られる「断片」に? 各フラグメントがキャプチャを実行した場合はどうすればよいですか? バンピングはどうですか? しかし、私たちが自分で創造することができたような困難はありません! 私は熱心に仕事に取り掛かりました...



マニュアル日没
もちろん、まず第一に、 ドラフトのようなゲームにZoGで使用される部分移動メカニズムを使用しようとしました。 ごく最近では、非常に難しいゲームを作成する過程で、私にとって重宝しました。 今まで、Axiomで使用する必要はありませんでしたが、すべてが初めて発生します。 部分的な動きの本質は、複雑な複合的な動きが小さなステップに分割されることです。 チェッカーでは、このような部分的な動きで相手の駒を取ることが実装されます。 同時に、コース実行のいわゆる「モード」も使用されます。これにより、キャプチャ完了するために次の部分移動も必要であることを示すことができます。



ZoGでの複合的な動きの実装に満足していないのはそのためです。 まず、ZoGを理解する上で、部分的な移動は、個別の独立したアクションにすぎません。 基本的に、それは同じプレイヤーが次々に実行する一連の動きです。 部分的な移動の間に中間情報を転送することはできません! グローバルフラグと位置フラグは、各ターンの開始時に自動的にリセットされます。 これは悪魔的な不快感ですが、これは問題の一部にすぎません! ZoGは、複合移動を単一のエンティティと見なすことはできません(特に、このため、「 最大キャプチャ 」ハードコードオプションを導入して「多数決」ルールを実装する必要がありました。このハードコードに適合しない他のアイデアは実装できません!







これは、Claude Leroyによって発明されたManaゲームの一部です。 各位置のダッシュの数は、フィギュアが移動できるステップ数を示します。 正確な数のステップを実行する必要があり、その際、運転中に引き返すことはできません。 これは待ち伏せが私たちを待っているところです! 非常にまれですが、2つのステップを完了した人物が「行き止まり」に陥ることがあります。 彼女は動きを続けることができません。なぜなら、他の部分が彼女に干渉し、動きを完了しなければならないので、次の一歩を踏み出す義務があるからです! そして、ZoGはこの問題を解決する手段をまったく提供していません!



もう1つの制限は、前の部分移動によって移動された同じピースのみが複合移動を続行できることです。 それはチェッカーでまさに起こることですが、「チェイス」では状況はもう少し複雑です。 たとえば、 バンピングを使用してキャプチャを実行できます。つまり、移動を実行したフィギュアではありません。 商工会議所では 、さらに困難です。 どちらの断片も敵の駒を奪い取り、論理的には次の部分移動を実行する権利があります。 そして、彼らは両方とも商工会議所に入った人物ではありません(ボード上の人物はもはや存在しません)!



単語を減らし、コードを増やします。
 : val ( -- n ) piece-type mark - ; : mirror ( 'dir -- 'dir ) DUP ['] nw = IF DROP ['] sw ELSE DUP ['] ne = IF DROP ['] se ELSE DUP ['] sw = IF DROP ['] nw ELSE ['] se = verify ['] ne ENDIF ENDIF ENDIF ; : step ( 'dir -- 'dir ) DUP EXECUTE NOT IF mirror DUP EXECUTE verify ENDIF ; : bump ( 'dir -- ) BEGIN here E5 <> verify friend? here from <> AND IF piece-type SWAP step SWAP create-piece-type FALSE ELSE TRUE ENDIF UNTIL DROP ; : slide ( 'dir n -- ) alloc-path ! val SWAP BEGIN step SWAP 1- DUP 0= IF TRUE ELSE my-empty? verify SWAP FALSE ENDIF UNTIL DROP from here move + enemy? IF + cont-type partial-move-type + ENDIF bump enemy? IF alloc-all ELSE alloc-path @ 0= verify ENDIF add-move ;
      
      







最終的には、敵のピースを取得するときに( 衝突を行う前に) 部分移動タイプの呼び出しを追加することになります。 上記の制限は引き続き有効です。 動きが始まった人物によってキャプチャが実行されなかった場合( Chamberでの衝突または「分裂」の結果)、部分的な動きを実行することはできませんが、この形式でもこのコードは良い解決策になります。 彼が稼いだ場合:









このrebusを解読できず、コードをAxiom開発者に送信しました。 グレッグはまだ回答していませんが、彼は問題を解決することを望んでいるパッチに取り組んでいるようです。 奇妙なことは、Axiomの部分的な動きが本当に機能するということです! さらに、ZRFの機能を大幅に拡張します。 これらはすべてドキュメントで詳しく説明されており、いくつかのアプリケーションで使用されています。 どうやら、私はちょうど運が悪かった。



部分的な移動が機能しなかったため、問題を解決する別の方法を探す必要がありました。 1回の動きですべてのアクションを完了できない場合は、いくつかの動きでストレッチを試みることができます! 私はすでに他のゲームでこれを行っており、ボード上に特別な不可視の位置を作成し、その上に旗のフィギュアを配置しました。 ピースが相手のものである場合、プレーヤーは自分のターンをスキップする必要があることを知っていました。 これは小さな変更ですが、他の人を惹きつけました。 移動を続けたピースマークする必要があり(移動を開始したピースだけでなく)、移動の転送順序を複雑にしなければなりませんでした。 全体として、それはかなり面倒で非常に厄介な決定でした。



私の努力の結果、ゲームの非常にオリジナルの修正が行われましたが、残念ながらオリジナルとの共通点が少なすぎました。 さらに、AIの「インテリジェンス」によって、「複雑な」 ターンオーダーの使用が打ち負かされました。 彼が使用するミニマックスアルゴリズムは、そのような自由に非常に否定的に反応し、「免疫」 検索エンジン (Axiom AIの代替構成)では、深さ検索を実装することは非常に困難です。



パンくず用



さて、私たちは自分で対戦相手を1つ(または2つ)取り、その後、若い方から順に残りのポイントに応じてポイントを分配します。 しかし、若い人物が複数いる場合はどうでしょうか? たとえば、ゲームの最初の段階では、各プレーヤーには2つの「1」があります。 1〜5ポイントの額面を持つ任意の数字を取得すると、ポイントの分布に2つのオプションが与えられ、どちらを選択するかによってゲームのコースが大きく変わる可能性があります。



同じ組み合わせ論
ここでは、ほとんど青から、興味深い組み合わせの問題が発生します。 ポイントをどのように(ピースを取得するときに)分配できるかを理解するためには、ゲームに表示される(プレイヤーの1人の側の)数字のすべての組み合わせを想像する必要があります。 次の3つの条件のみがあります。



  1. 各図は、1〜6ポイントの額面を持つことができます。
  2. 数字の数は10を超えることはできません
  3. ポイントの合計数は常に25です


この問題には美しい分析ソリューションがあることは間違いありません(おそらく読者が教えてくれるでしょう)が、私はそれを探し始めませんでした。 スクリプトをコンパイルして、指定された条件を満たす形状のすべての可能なセットを生成しました。 セット内の数字は額面順に並べられます。これは、取得したポイントがこの順序で分布するためです。



スクリプト
 my @d; my %s; sub out { my ($deep) = @_; for (my $i = 0; $i < $deep; $i++) { print "$d[$i]"; } print "\n"; } sub dice { my ($start, $deep, $sum) = @_; if ($sum == 25) { out($deep); } if ($deep < 10 && $sum < 25) { for (my $i = $start; $i <= 6; $i++) { $d[$deep] = $i; dice($i, $deep + 1, $sum + $i); } } } dice(1);
      
      







結果
 1111111666 1111112566 1111113466 1111113556 1111114456 1111114555 1111122466 1111122556 1111123366 1111123456 1111123555 1111124446 1111124455 111112666 1111133356 1111133446 1111133455 1111134445 111113566 1111144444 111114466 111114556 111115555 1111222366 1111222456 1111222555 1111223356 1111223446 1111223455 1111224445 111122566 1111233346 1111233355 1111233445 1111234444 111123466 111123556 111124456 111124555 1111333336 1111333345 1111333444 111133366 111133456 111133555 111134446 111134455 11113666 111144445 11114566 11115556 1112222266 1112222356 1112222446 1112222455 1112223346 1112223355 1112223445 1112224444 111222466 111222556 1112233336 1112233345 1112233444 111223366 111223456 111223555 111224446 111224455 11122666 1112333335 1112333344 111233356 111233446 111233455 111234445 11123566 111244444 11124466 11124556 11125555 1113333334 111333346 111333355 111333445 111334444 11133466 11133556 11134456 11134555 11144446 11144455 1114666 1115566 1122222256 1122222346 1122222355 1122222445 1122223336 1122223345 1122223444 112222366 112222456 112222555 1122233335 1122233344 112223356 112223446 112223455 112224445 11222566 1122333334 112233346 112233355 112233445 112234444 11223466 11223556 11224456 11224555 1123333333 112333336 112333345 112333444 11233366 11233456 11233555 11234446 11234455 1123666 11244445 1124566 1125556 113333335 113333344 11333356 11333446 11333455 11334445 1133566 11344444 1134466 1134556 1135555 1144456 1144555 115666 1222222246 1222222255 1222222336 1222222345 1222222444 122222266 1222223335 1222223344 122222356 122222446 122222455 1222233334 122223346 122223355 122223445 122224444 12222466 12222556 1222333333 122233336 122233345 122233444 12223366 12223456 12223555 12224446 12224455 1222666 122333335 122333344 12233356 12233446 12233455 12234445 1223566 12244444 1224466 1224556 1225555 123333334 12333346 12333355 12333445 12334444 1233466 1233556 1234456 1234555 1244446 1244455 124666 125566 133333333 13333336 13333345 13333444 1333366 1333456 1333555 1334446 1334455 133666 1344445 134566 135556 1444444 144466 144556 145555 16666 2222222236 2222222245 2222222335 2222222344 222222256 2222223334 222222346 222222355 222222445 2222233333 222223336 222223345 222223444 22222366 22222456 22222555 222233335 222233344 22223356 22223446 22223455 22224445 2222566 222333334 22233346 22233355 22233445 22234444 2223466 2223556 2224456 2224555 223333333 22333336 22333345 22333444 2233366 2233456 2233555 2234446 2234455 223666 2244445 224566 225556 23333335 23333344 2333356 2333446 2333455 2334445 233566 2344444 234466 234556 235555 244456 244555 25666 33333334 3333346 3333355 3333445 3334444 333466 333556 334456 334555 344446 344455 34666 35566 444445 44566 45556 55555
      
      







294の可能なオプションのみ。 しかし、これは戦いの半分に過ぎません。 レイアウト自体ではなく、どのようにしてそれぞれの図形にポイントを配置できるかに興味があります。 詳細な推論で読者を退屈させることはせず、スクリプトとその作業の最終結果のみを示します。



スクリプト
 my @d; my %s; sub out { my ($deep) = @_; for (my $i = 0; $i < $deep; $i++) { print "$d[$i]"; } print "\n"; } sub proc { my ($x, $r, $m) = @_; if ($x == 0) { $s{$r}++; } else { my $n = $x % 10; for (my $i = 0; $i < $n; $i++) { proc(int($x / 10), $r + $i * $m, $m * 10); } } } sub alloc { my ($x, $deep, $res) = @_; if ($x == 0) { proc($res, 0, 1); } else { my $vl = 6; for (my $i = 0; $i < $deep; $i++) { if ($d[$i] < $vl) { $vl = $d[$i]; } } if ($vl < 6) { my $cn = 0; my $ix = 0; for (my $i = 0; $i < $deep; $i++) { if ($d[$i] == $vl) { $cn++; $ix = $i; } } my $y = $d[$ix]; $d[$ix] = 6; $x -= 6 - $vl; if ($x < 0) { $x = 0; } alloc($x, $deep, $res * 10 + $cn); $d[$ix] = $y; } } } sub dice { my ($start, $deep, $sum) = @_; if ($sum == 25) { for (my $i = 0; $i < $deep; $i++) { my $x = $d[$i]; $d[$i] = 6; alloc($x, $deep, 0); $d[$i] = $x; } } if ($deep < 10 && $sum < 25) { for (my $i = $start; $i <= 6; $i++) { $d[$deep] = $i; dice($i, $deep + 1, $sum + $i); } } } dice(1, 0, 0); my $all; foreach my $k (sort { $s{$a} <=> $s{$b} } keys %s) { $all += $s{$k}; print "$k\t=> $s{$k}\n"; } print "\n$all\n";
      
      





結果
 102 => 1 331 => 1 200 => 1 ... 22 => 93 5 => 106 21 => 152 20 => 152 11 => 152 10 => 220 4 => 259 3 => 584 2 => 1061 1 => 1677 0 => 2407 7954
      
      







— , . , «20» , ( 0), , . , , «» , «3333445» (, «» «»). , , «» , 30% (2407/7954) , , 64%!



特にこのような場合、ZoGは興味深いインターフェース機能を提供します。移動を実行すると、プレーヤーは開始と終了の2つのフィールドを示します。選択したフィールドのペアをつなぐいくつかの異なる可能な動きがある場合、プレイヤーには選択する機会が与えられます(ポップアップメニュー)。最も単純な例は、チェスのポーンの変換です最後の水平に到達すると、ポーンは任意のピース(ビショップからクイーンまで)に変わる可能性があり、プレイヤーが選択する必要があります。このオプションを使用することにしました。



ヘンゼルとグレーテルに!
— , ZoG , , ZSG-. , . , , , . , ( ) 10, . . ( , ), . , . 0, .



 VARIABLE alloc-path VARIABLE alloc-val VARIABLE alloc-target VARIABLE alloc-pos : alloc-to ( pos -- ) DUP add-pos DUP val-at 6 SWAP - DUP alloc-val @ > IF DROP alloc-val @ 0 alloc-val ! ELSE alloc-val @ OVER - alloc-val ! ENDIF my-next-player ROT ROT OVER piece-type-at + SWAP create-player-piece-type-at ; : alloc ( -- ) 6 0 BEGIN DUP enemy-at? OVER not-in-pos? AND IF SWAP OVER val-at MIN SWAP ENDIF 1+ DUP A9 > UNTIL DROP DUP 6 < IF alloc-target ! alloc-path @ 10 MOD alloc-pos ! 0 BEGIN DUP enemy-at? OVER not-in-pos? AND IF DUP val-at alloc-target @ = IF alloc-pos @ 0= IF DUP alloc-to 0 alloc-target ! DROP A9 ELSE alloc-pos -- ENDIF ENDIF ENDIF 1+ DUP A9 > UNTIL DROP alloc-target @ 0= verify alloc-val @ 0> IF alloc-path @ 10 / alloc-path ! RECURSE ENDIF ELSE DROP ENDIF ; : alloc-all ( -- ) 0 pos-count ! here add-pos alloc ;
      
      







alloc-path « ». , 105 , , . , 4 . , :



 : eat ( 'dir n -- ) LITE-VERSION NOT IF check-pass check-neg ENDIF + alloc-path ! val SWAP BEGIN step SWAP 1- DUP 0= IF TRUE ELSE my-empty? verify SWAP FALSE ENDIF UNTIL DROP from here move LITE-VERSION NOT enemy? AND IF from piece-type-at mark - ABS mark SWAP - create-piece-type ENDIF bump DROP here E5 <> verify enemy? verify LITE-VERSION NOT IF clear-neg set-pass ENDIF + val alloc-val ! + alloc-all add-move ; : eat-nw-0 ( -- ) ['] nw 0 eat ; : eat-sw-0 ( -- ) ['] sw 0 eat ; : eat-ne-0 ( -- ) ['] ne 0 eat ; : eat-se-0 ( -- ) ['] se 0 eat ; : eat-w-0 ( -- ) ['] w 0 eat ; : eat-e-0 ( -- ) ['] e 0 eat ; : eat-nw-1 ( -- ) ['] nw 1 eat ; : eat-sw-1 ( -- ) ['] sw 1 eat ; : eat-ne-1 ( -- ) ['] ne 1 eat ; : eat-se-1 ( -- ) ['] se 1 eat ; : eat-w-1 ( -- ) ['] w 1 eat ; : eat-e-1 ( -- ) ['] e 1 eat ; : eat-nw-2 ( -- ) ['] nw 2 eat ; : eat-sw-2 ( -- ) ['] sw 2 eat ; : eat-ne-2 ( -- ) ['] ne 2 eat ; : eat-se-2 ( -- ) ['] se 2 eat ; : eat-w-2 ( -- ) ['] w 2 eat ; : eat-e-2 ( -- ) ['] e 2 eat ; : eat-nw-3 ( -- ) ['] nw 3 eat ; : eat-sw-3 ( -- ) ['] sw 3 eat ; : eat-ne-3 ( -- ) ['] ne 3 eat ; : eat-se-3 ( -- ) ['] se 3 eat ; : eat-w-3 ( -- ) ['] w 3 eat ; : eat-e-3 ( -- ) ['] e 3 eat ; {moves p-moves {move} split-nw-0 {move-type} normal-priority {move} split-ne-0 {move-type} normal-priority {move} split-sw-0 {move-type} normal-priority {move} split-se-0 {move-type} normal-priority {move} split-w-0 {move-type} normal-priority {move} split-e-0 {move-type} normal-priority {move} split-nw-1 {move-type} normal-priority {move} split-ne-1 {move-type} normal-priority {move} split-sw-1 {move-type} normal-priority {move} split-se-1 {move-type} normal-priority {move} split-w-1 {move-type} normal-priority {move} split-e-1 {move-type} normal-priority + {move} eat-nw-0 {move-type} normal-priority + {move} eat-ne-0 {move-type} normal-priority + {move} eat-sw-0 {move-type} normal-priority + {move} eat-se-0 {move-type} normal-priority + {move} eat-w-0 {move-type} normal-priority + {move} eat-e-0 {move-type} normal-priority + {move} eat-nw-1 {move-type} normal-priority + {move} eat-ne-1 {move-type} normal-priority + {move} eat-sw-1 {move-type} normal-priority + {move} eat-se-1 {move-type} normal-priority + {move} eat-w-1 {move-type} normal-priority + {move} eat-e-1 {move-type} normal-priority + {move} eat-nw-2 {move-type} normal-priority + {move} eat-ne-2 {move-type} normal-priority + {move} eat-sw-2 {move-type} normal-priority + {move} eat-se-2 {move-type} normal-priority + {move} eat-w-2 {move-type} normal-priority + {move} eat-e-2 {move-type} normal-priority + {move} eat-nw-3 {move-type} normal-priority + {move} eat-ne-3 {move-type} normal-priority + {move} eat-sw-3 {move-type} normal-priority + {move} eat-se-3 {move-type} normal-priority + {move} eat-w-3 {move-type} normal-priority + {move} eat-e-3 {move-type} normal-priority {move} slide-nw {move-type} normal-priority {move} slide-ne {move-type} normal-priority {move} slide-sw {move-type} normal-priority {move} slide-se {move-type} normal-priority {move} slide-w {move-type} normal-priority {move} slide-e {move-type} normal-priority -( {move} exchange-1-nw {move-type} normal-priority - {move} exchange-1-ne {move-type} normal-priority - {move} exchange-1-sw {move-type} normal-priority - {move} exchange-1-se {move-type} normal-priority - {move} exchange-1-w {move-type} normal-priority - {move} exchange-1-e {move-type} normal-priority - {move} exchange-2-nw {move-type} normal-priority - {move} exchange-2-ne {move-type} normal-priority - {move} exchange-2-sw {move-type} normal-priority - {move} exchange-2-se {move-type} normal-priority - {move} exchange-2-w {move-type} normal-priority - {move} exchange-2-e {move-type} normal-priority - {move} exchange-3-nw {move-type} normal-priority - {move} exchange-3-ne {move-type} normal-priority - {move} exchange-3-sw {move-type} normal-priority - {move} exchange-3-se {move-type} normal-priority - {move} exchange-3-w {move-type} normal-priority - {move} exchange-3-e {move-type} normal-priority - {move} exchange-4-nw {move-type} normal-priority - {move} exchange-4-ne {move-type} normal-priority - {move} exchange-4-sw {move-type} normal-priority - {move} exchange-4-se {move-type} normal-priority - {move} exchange-4-w {move-type} normal-priority - {move} exchange-4-e {move-type} normal-priority - {move} exchange-5-nw {move-type} normal-priority - {move} exchange-5-ne {move-type} normal-priority - {move} exchange-5-sw {move-type} normal-priority - {move} exchange-5-se {move-type} normal-priority - {move} exchange-5-w {move-type} normal-priority - {move} exchange-5-e {move-type} normal-priority ) moves}
      
      







, Axiom ( ). ? ! , . ( exchange -), . , .



厳密に言えば、これは完全に正しい解決策ではありません。 「チェイス」のルールによれば、ポイントを分配することは、動きをしたプレイヤーではなく、彼の対戦相手でなければなりません。 ZoGを使用してこれを達成する方法はわかりませんが、非常に簡単な回避策があります。 ZoGインターフェースは、ボードを編集するための便利なインターフェースを提供します。ポップアップメニューコマンドを使用して、プレーヤーはボード上のピースを削除したり、別のピースを作成したりできます。この機能はデバッグに不可欠であり、よく使用します。一般に、ポイントの自動配布が気に入らなかったプレイヤーは、簡単に手動でポイントを再配布できます(ムーブのシーケンスに違反することはありません)。最小限の注意のみを守ってください。編集プロセス中、プレーヤーの1人が5個未満の場合は許可しないでください。この場合、彼はすぐに敗北と認められ、ゲームは停止します。



... 1つに数えます!



食べられたポイントの「可変」分布のアイデアが失敗したので、私はZRFを通してゲームの開発に戻りました。原則として、Axiomの実装も機能しましたが、AIがまだありません(Axiomは標準のZoG-ovskyを使用できません)。一般的に、このタスクは評価関数の正しいコーディングに要約されます(麻酔用の「カスタムエンジンもあります)が、これも簡単ではありません!いずれにせよ、Chaseの移動性と物質収支を考慮に入れた標準的な評価関数は最良であるとは証明されませんでした。



いくつかの詳細
, , :

 : OnEvaluate ( -- score ) mobility current-player material-balance KOEFF * + ;
      
      





mobility . — , . , , — , , :

 : mobility ( -- score ) move-count current-player TRUE 0 $GenerateMoves move-count - $DeallocateMoves ;
      
      





, «» « », . — , . , , Axiom :

 {pieces {piece} p1 {moves} p-moves 6 {value} {piece} p2 {moves} p-moves 5 {value} {piece} p3 {moves} p-moves 4 {value} {piece} p4 {moves} p-moves 3 {value} {piece} p5 {moves} p-moves 2 {value} {piece} p6 {moves} p-moves 1 {value} pieces}
      
      





«» , . , , ! AI . , . , / , ( , ), Chamber ., , ZRF. AI ZoG- , .



些細なことは1つしかありませんでした-ZRFには算術演算がまったくありませんでした!チェイスは、常に数えなければならないゲームです!場合によっては、外に出ることができます。たとえば、すべての数字でポイント(最大25)をカウントする代わりに、プレーヤーの敗北を判断するときに、数字の数の標準チェックに制限することができます。 25ポイントは明らかに4つのフィギュアに置くことは不可能であり、さらに多くのフィギュアに分配することは常に可能であるため、ゲームを完了するための次の条件で十分です。



 (loss-condition (Red White) (pieces-remaining 4) ) (loss-condition (Red White) (pieces-remaining 3) )
      
      





ゲームでは、1つの動きで2つのピースが同時に取られる場合(チャンバー内でピースを分割した後)に状況が発生する可能性があるため、2番目のチェックが必要です。残念ながら、整数演算が必要な問題が1つあります!もちろん、これは「食べられた」ポイントの分布です。ZRFでは、選択できるいくつかの可能な配信オプションを提供しようとはしていません。一番若いものから始めて、すべての数字を調べて、まだ分布していないポイントを正しく追加するだけです。ここに私がそれをする方法があります:



主にスティックから
( ). ZRF- , ( ). ( ) . , «», ( ) :



/
 (define clear (set-flag $1-8 false) (set-flag $1-4 false) (set-flag $1-2 false) (set-flag $1-1 false) ) (define inc (if (flag? $1-1) (set-flag $1-1 false) (if (flag? $1-2) (set-flag $1-2 false) (if (flag? $1-4) (set-flag $1-4 false) (if (flag? $1-8) (set-flag $1-8 false) else (set-flag $1-8 true) ) else (set-flag $1-4 true) ) else (set-flag $1-2 true) ) else (set-flag $1-1 true) ) ) (define dec (if (not-flag? $1-1) (set-flag $1-1 true) (if (not-flag? $1-2) (set-flag $1-2 true) (if (not-flag? $1-4) (set-flag $1-4 true) (if (not-flag? $1-8) (set-flag $1-8 true) else (set-flag $1-8 false) ) else (set-flag $1-4 false) ) else (set-flag $1-2 false) ) else (set-flag $1-1 false) ) )
      
      







— :



!
 (define not-10? (or (not-flag? $1-8) (flag? $1-4) (not-flag? $1-2) (flag? $1-1) ) ) (define calc (clear x) mark START (while (on-board? next) next (if friend? (inc x) ) ) (verify (not-10? x)) back )
      
      







, , . , . . ZRF — , - !



初期化
 (define init (clear $1) (if (or (piece? p1) (piece? p3) (piece? p5)) (set-flag $1-1 true) ) (if (or (piece? p2) (piece? p3) (piece? p6)) (set-flag $1-2 true) ) (if (or (piece? p4) (piece? p5) (piece? p6)) (set-flag $1-4 true) ) )
      
      







, . ( , ), , , , «». ! :



-
 (define sum (while (not-0? $2) (inc $1) (dec $2) ) )
      
      







, . «» ? , , ?



 (define try-alloc (if (is-0? x) (inc y) else (dec x) ) ) (define set-piece (if (am-i-red?) (create White $1) else (create Red $1) ) ) (define alloc-to (clear y) (if (piece? p1) (try-alloc) (try-alloc) (try-alloc) (try-alloc) (try-alloc) ) (if (piece? p2) (try-alloc) (try-alloc) (try-alloc) (try-alloc) ) (if (piece? p3) (try-alloc) (try-alloc) (try-alloc) ) (if (piece? p4) (try-alloc) (try-alloc) ) (if (piece? p5) (try-alloc) ) (if (is-0? y) (set-piece p6) else (if (is-1? y) (set-piece p5) else (if (is-2? y) (set-piece p4) else (if (is-3? y) (set-piece p3) else (set-piece p2) ) ) ) ) ) (define alloc (if (not-0? x) mark ST (while (on-board? next) next (if (and enemy? (piece? $1) (not-0? x) (not-position-flag? is-captured?)) (alloc-to) ) ) back ) ) (define alloc-all (alloc p1) (alloc p2) (alloc p3) (alloc p4) (alloc p5) )
      
      







alloc-all , x ( — 12, ). x 0, , p1 p5 ( , , ). alloc-to . . , ( p1 5 . p2 — 4 ..). , x , — y . ( 4), , .









その結果、すべての「異常な算術」は非常に許容可能なパフォーマンスで動作し、AIはまったく影響を受けません。そのような実験は常に成功するとは限らない。たとえば、このバージョンの電卓(ZRFには算術演算がないことを思い出します)は、冗談としか見なせません。そのパフォーマンスはひどいです!しかし、私たちの場合、「異常なプログラミング」が最善の解決策であることが証明されました。




All Articles