ボードゲームスクリプト言語のアプローチ

たまたま自分でゲームを書いたのは偶然で、プロとして扱ったことは一度もありません。

しかし、DSL(ドメイン固有言語)を作成して完全に異なるコードを作成するルーチンを減らす経験は、少なくとも一部です。

これはまさに私が共有したいものです。広大さをどのように調整するか。







優れたハブユーザーGlukKazanは、さまざまなボードゲームを作成するためのすばらしい製品について多くの記事を書いています。 Zillions of GamesAxiom Development Kitなど

ただし、これらのプログラムは普遍的ではありません。 そして、あなたは常にそれらを改善したいです。 ただし、これらの製品は無料ではないため、ソフトウェア製品を再度作成する必要があります。

GlukKazanは 、オープンソースの Dagazプロジェクトに取り組んでいます。このプロジェクトは、優れた記事で共有されています(たとえば、 Dagaz:A new begin )。



したがって、ボードゲーム用のユニバーサルゲームエンジンを作成し、ゲームのルールをエンジンに説明するのに役立つスクリプト言語を基礎として使用するとします。

どうやって彼に会いたいの?

1)ゲームのほとんどすべてのルールを記述するために、言語は可能な限り普遍的でなければなりません。

2)しかし、言語は可能な限りシンプルで、最小限の構成でなければなりません。

3)ルールの説明は、イグロデルを読み、ゲームを書くのが簡単でなければなりません

4)ほとんどの場合、すでに書かれたものを補足/変更することでゲームを書くことができます

5)スクリプトとの通信(API)はできる限り単純でなければなりません。 ボットとAIを簡単に作成できるようにします。

一見したところ、ルーチンを減らすことはできず、すぐに準備できるゲームを書く方が簡単なので、誰も費やした労力をまったく必要としないようです。

しかし、これはそうではありません。

すべてがはるかに簡単です!



デミウルジとブラックボックス



さて、私たちは中傷者であり、このスクリプト言語を既に作成したことがあると想像してみましょう。 はい、はい、すでに。 この言語で何ができ、何ができるのか見てみましょう。



制限を置かないでください



たとえば、 Dagazプロジェクト(およびその前身であるAxiomおよびZoG )はボードゲームに焦点を当てています。 ただし、ボードゲームと非ボードゲームの違いを人に説明するのは非常に困難です。 あるプログラミング言語の正確な定義でそれを説明することは、さらに難しいです。

したがって、最初の最も重要なルール-存在しない場所に制限を加えないでください!

ボードゲームは考慮しませんが、ボードゲームは考慮します。

次のリストを見てみましょう。これについて説明し、エンジンで遊んでみましょう。



それらは一見非常に異なっています。 それらすべてを統合するものは何ですか? 本当に何かありますか?

はい それらを結びつけるのは

  1. 1人のプレーヤーが常に一度に行く
  2. ほとんどの場合、プレーヤーは無限に長い時間考えることができます(1移動あたりの時間制限がある場合は個別に検討します)
  3. 毎回、可能なアクションの数が限られています。 ポイントのゲームは例外です(ポイントは無限のボード上のどこにでも配置できるため)。 便宜上、ポイントを少し減らしますが、エンジンは変更しません


すべて、エンジンに対するこれらの制限のみ!

したがって、本質的には、2つの主要な質問をすることができるはずのエンジンが必要です。誰が今歩いているのか、彼に何ができるのか。

IN: who OUT: Player1 IN: actions OUT: [SomeAction1, SomeAction2 Param21, ...]
      
      





また、エンジンは、選択したアクションを完了するために、1つのアクションを実行できる必要があります。

 action SomeAction3
      
      





おそらく、もう1つのアクションは予備アクションを選択することです(たとえば、移動に割り当てられた時間が終了し、移動がまだ行われていない場合は、ゲームを終了したりランダムアクションを選択したりしないように、この場合は予備アクションを選択できます)。

 preaction SomeAction5
      
      





とにかく、誰かがそれらを回避したいので、これ以上の制限を置く必要はありません。



広く考える



しかし、ポーカーを書くとどうなりますか? フールではカードを投げることができますが、投げることはできません。 パスと言ったり、スーツを選んだりすることもアクションです。 カルカソンヌにフィールドを置くこともアクションです。

テトリスはどこですか? テトリスは、リアルタイムゲームであり、ほとんど必要ないため、おそらく非常に困難です。 エンジンの変更は簡単ですが、関係ありません。



スクリプト言語を去勢しないでください



原則に従うことはお勧めしません。スクリプトからすべてのデータを取得し、エンジンでモデル化します。 この方法では、 .iniファイルを構成として使用する方が簡単であり、スクリプト言語を悪化させないのは理にかなっているためです。

そして、彼が戦ったときに愚か者のためにカードを得る方法は? 次は再び歩きますが、移動の選択肢は1つだけです-カードを取ります。 車のアクションに値を追加できる場合は、プレーヤーに選択するオプションを尋ねないでください。

 IN: who OUT: Player3 #  8  (     10 -   ) IN: actions OUT: [Attack 8Diamonds, Attack 10Diamonds 10Hearts, ...] IN: action Attack 8Diamonds OUT: action Attack 8Diamonds IN: who OUT: Player4 #   ,   ?  IN: actions OUT: [TakeTable, PassShowOnly QueenDiamonds, Defend QueenDiamonds, Pass 6Diamonds, PassShowOnly 6Diamonds,...] IN: action Pass 6Diamonds OUT: action Pass 6Diamonds IN: who OUT: Player1 #     ,     IN: actions OUT: [TakeTable, Defend KingDiamonds 6Diamonds, Defend KingDiamonds 8Diamonds, Defend AceDiamonds 6Diamonds, Defend AceDiamonds 8Diamonds, PassShowOnly KingDiamonds, Pass KingDiamonds, PassShowOnly AceDiamonds, Pass AceDiamonds] IN: action Defend AceDiamonds 6Diamonds OUT: action Defend AceDiamonds 6Diamonds IN: who OUT: Player3 #   3  IN: actions OUT: [Pass, Attack AceHearts] IN: action Pass OUT: action Pass IN: who OUT: Player2 #  ,  ,   IN: actions OUT: [auto Pass] IN: action Pass OUT: action Pass IN: who OUT: Player4 #  IN: actions OUT: [auto Pass] IN: action Pass OUT: action Pass IN: who OUT: Player1 #       IN: actions OUT: [Defend KingDiamonds, TakeTable] IN: action Defend KingDiamonds OUT: action Defend KingDiamonds IN: who OUT: Player3 #   1 ,      IN: actions OUT: [auto Take AceHearts] IN: action Take AceHearts OUT: action Take AceHearts IN: who OUT: Player1 #   2    IN: actions OUT: [auto Take 6Hearts 10Clubs] IN: action Take 6Hearts 10Clubs OUT: action Take 6Hearts 10Clubs
      
      





エンジンにゲームについて何も知らせないでください。 クエリと美しいディスプレイのための単なるコンベヤです。 これ以上ではありませんが、それ以下です。



設定なしの場所



スクリプトとのコミュニケーションがどれほど簡単であっても、設定なしで管理することはできません。

愚か者は、3、4、4、5、さらには6で一緒に遊ぶことができます。 それぞれ2x2、3x3でプレイできます。

設定を送信する必要があります。 別のコマンドを追加します。 わかりました、2つ、ステータスを確認する必要があります

 set players = 4 set groups = 2x2 set startFrom = Player3 IN: get name OUT: name = 15-puzzle
      
      







ゲームファースト



ゲームを作成することを覚えておく必要があります。 そして、どんなゲームも



新しい能力を反映したチームをさらに作成しましょう-ゲームをロードし、ゲームを開始し、ゲームの結果を調べます

 IN: load /path/chess.game OUT: load /path/chess.game IN: start OUT: start IN: result OUT: Finished ; Win Player1; Details Player1 78, Player2 38 OUT: Loaded /path/chess.game OUT: Started
      
      







カツレツからハエを分けます。 視覚化言語



過度の汎用性を追いかけないでください。

ボットはドラムを視覚化しますが、その人は非常にうるさく、非常に美しい写真を見たいと思います。

これによる主な結論は、視覚化言語とメッセージング言語は一般的に異なるということです。 タスクが異なるため、スクリプト言語の普遍性を追いかけることは価値がありません。

視覚化メッセージ言語は、交換言語に合わせてカスタマイズする必要はありません。

視覚化に必要なオブジェクトごとに、どの画像(またはどの画像)を表示するか、画面のどの部分、何と何が重なるかを記述する視覚化形式(以下PVと呼びます)が作成されているとします。

最初にすべてを表示する必要があります。 そのため、状況を視覚化するためのチームが必要です。チームは、PV形式のオブジェクトのリストを作成します。

結局のところ、駒のあるチェス盤、または愚か者のために私たちに与えられたものが見えるはずです。

 IN: view OUT: [ (Piece1, ), (King, ), ..... ]
      
      





ところで、多くの場合、ゲーム自体に数字として表示されないものを視覚化する必要があります。たとえば、フールの場合は、ライバルが閉じているカードの数とデッキを表示する必要があります。

さらに、可能な動きを視覚化する方法を知る必要があります。

つまり、すべての可能なアクションのリストを含むアクションを視覚化するチームがあり、2つのオプションのいずれかに新しい説明があります:完全な説明または部分的な説明(必要な表示図形の追加、および既存の図形からの図形の削除)。

たとえば、フールでは、選択したカードをデッキから削除する必要がありますが、このカードはテーブルに表示されます。

 IN: view actions OUT: [(Attack 8Diamonds, Diff [remove Card5, add (Card5, ), ... ]), ... ]
      
      





チェスでは、選択したピースが「選択された」色になり、選択した正方形の場所に同じピースが表示されます。 さらに、攻撃が発生した場合、beatられた姿は消えます。

さらに、可能なアクションをいつ表示するかを知る必要があります。

実際には、前のコマンドに挿入するか、新しいコマンドを追加します。

 IN: view actions OUT: [(Attack 8Diamonds, Diff [remove Card5, add (Card5, ), ... ], OnChoose Card5), (Pass, Diff [add PassWord], OnChoose PassLabel), ... ]
      
      





人工知能言語


AIモジュールは個別に作成することが望ましいですが、共通のスクリプトに統合されています。

原則として、会話AIはそれほど難しくありません。

 IN: analize OUT: [75 Pass, 44 Attack 6Diamonds, 59 Attack 8Diamonds] IN: analize quick OUT: [10 Pass, 5 Attack 6Diamonds, 20 Attack 8Diamonds]
      
      





これを実装することは、説明するよりもはるかに困難です。 フォークが必要かもしれません。



相互理解



エンジンは、スクリプトとの会話に誤りがあるかどうかを理解する必要があります。 特に相互作用がクライアント/サーバーアプリケーションを表す場合、通信は相互に行われる必要があります。

基本的に、1つのチームが必要です。最後のチームが承認されました。

 IN: it OUT: result
      
      





回答が重要でない場合は、同じコマンドに回答する必要があります。 つまり、インストール、実行されたアクション。

そして、誰かが思考の時間制限を設定すると、プレイヤーは今度はスリープ状態になり、エンジンは偶然プレイヤーのように見えました。

エンジンがエラーで応答する可能性があることを忘れないでください。

 IN: action TakeTable OUT: ERROR action is out list IN: who OUT: ERROR game result is Finished
      
      







神は鍋を燃やしません



さて、私たちはスクリプトでできることと気にしないことの多くをたどりました。

すべてではなく、すべてのロジックがスクリプトに当てはまります。 だからこそ、私たちはあらゆるボードゲームの完全な普遍性を達成します。

ここでの妥協は制限につながります。タンバリンと一緒に踊らなければならないのは1回か2回以上だからです。

しかし、簡単でシンプルな言語が必要でした! そして、私たちはすべてをイグロデロフに置くためにすべてを提供されます。

できないが、本当に欲しいなら、できる! ゲームメーカーではなくプログラマーにとっては難しいことです。 そして、そうなるでしょう。



イグロデロフになる



自分で小さなゲームメーカーになろう!

私を驚かせるCの最も驚くべき機能の1つは、型文字列がライブラリ関数であることです。

ボード、フィールド、またはデッキのデッキを一気に思いつくまで待つ必要はありません。 これらの概念自体をスクリプト言語で記述します。

ここでは普通の人が書いてボードを取り、そのようなフィールドを作成します。

そのため、書かれたすべてのスクリプトを上部で変更できることが必要です。

ゲームに静的なボードは必要ありませんでしたが、動的なボードは移動中に毎回そのステータスがチェックされるようにボードを変更してください。

 # DynamicBoard import StaticBord let board { constuctor {let board = prevous board} on_player_change {let board = recalculate} }
      
      







帝王帝国帝、そして神なる神へ



プログラマー自身によって書かれたライブラリの束にもかかわらず、スクリプトコードは軽量になりますが、簡単ではありません。

不要なコードを削除する方法は?

言語自体で記述されたサブスクリプト(このスクリプト言語のDSL)が必要であることを理解する必要があります。

怖いように聞こえますが、すべてが描かれている悪魔ではありません。

私にとって、最も美しいライブラリの1つはHaskellのParsecパーサーです。 これは、Cでの文字列実装よりもずっときれいです。

言語に行を含めることを計画していましたが、Haskellのときにパーサーを作成する予定はありませんでした。

実際、彼らはパーサーをコンパイルするために言語レベル2のサブ言語のみを作成しました(実際、少なくとも4つの追加レベルがあります)。

1)トークン/キャラクターレベル。 このレベルで関数を書くことを気にする人はほとんどいません。これは大部分が理解できないもので、頭のあるライブラリ関数が十分にあります。

例えば

行末-eof

1トークン/シンボルを取ります-anyToken

パーサーを試してみて、もし落ちたら、トークンを消費しなかったふりをしてください-pを試してください

または:トークンを消費せずにクラッシュする場合は、2番目のパーサー-p1 <|> p2を使用します。

楽しみにしてください:パーサーが成功した場合、トークンを使用していないふりをしてパーサーを試してください-lookAhead p

(実際にはもっと多くの機能があります)。

これらのトークン関数の組み合わせにより、非常に複雑なパーサーを生成できます。

2)パーサーのレベル。 非常に直感的で、次のような機能の動物園全体が補完されています。

多くのパーサー- 多くのp

多く、少なくとも1つのパーサー-many1 p

パーサーの分割-p `sepBy` sepp

行- 文字列

パーサー間-p1 p2間

...



実際に、ライブラリのユーザーが到着すると、彼は「すべてが私たちの前に構築されている」ことを確認します 。既製のコンストラクターがあり、興味のあるレベルで必要なものを取得して収集します

同じことが私たちの言語にも当てはまります。 フィルター関数の作成方法について考える必要はありません。既にそこに記述されているはずです。

そのまま使用してください。スクリプト言語はユーザーに話しかける必要があります。

チェッカーが必要で、1つまたは2つのチェッカーがセルに収まるようにする場合-セルの説明のみを再作成する必要があります。

正方形のボードが欲しい-正方形をロードし、六角形が欲しい-6コーナーをロードする。

ボードからポーンを取り除くと、ボード自体のフィールドが破壊されるので、ボードを動的にし、ポーンの破壊を監視する必要があります。



インスタントスープまたは完璧に制限なし



私たちは皆、美味しく食べるのが大好きですが、誰もが美味しく料理できるわけではありません。 スキルと時間がかかります。

Igrodelyはスープが好きです。

彼らはまだ共有を容易にする必要があります。

Skipt言語ではまだ必要です...メタ言語。 息を吹き込んで、ゲームのテンプレートだけ。

チェスのようなゲーム、チェッカーのようなゲーム、愚か者のようなゲーム、カードゲーム、...

プログラミングを行わずに、既成のソリューションを簡単に構成する機会を提供します。

 import ChessLikeGame; let params = { previous params; players = 4; board_length = 75 symmetric start positions = true; start_white_positions = [A1, C1,D1, ....] }
      
      







おわりに



記事の冒頭のウィッシュリストを見ると、恐ろしいことがあります。超普遍的なスクリプト言語が必要でしたが、基本的に既製のソリューションの構成であり、書かれたゲームのロジックを簡単に変更できます。

実際、私たちは自分が望むもの、望まないものを理解し、書かれたコードの主要部分はスクリプト言語で書かれるべきであり、エンジンには視覚化と反応が残されるべきだと気づきました。



チェス、バルダ、そして愚か者の間には違いはありません。

誰が歩いていますか? プレイヤー1

 IN: who OUT: Player1
      
      





ドットとクリスタルの再描画のスポットに違いはありません。 毎回、アクションの限られた選択があります。

 # chess action Castle King Rook2 # checkers action Attack Men7 D2 F4 # splut action Pull Troll E5 D5 # points action Add 10-14 # carcassonn action Put Title1Tree Place8-17 #15-puzzle action Push 8 # dominos action Put Title1-6 Place4-1 Left # crystall painting action Color Yellow # durak action New QuenHearts
      
      





理解する必要があるだけで、これをコンピューターに説明する方法が明らかになります。



All Articles