web-dev-qa-db-ja.com

チェスのゲームを構築するときにオブザーバーパターンを使用することは良い考えですか?

UI要素を持つOOPの概念を使用してチェスゲームを設計しようとしています。私のアイデアは、選択されたときにピースが移動できる正方形/セルの数を示すことです。基本的に私はそれが移動/攻撃できるパス/方向を別の色で表示したいと思います。

次のようなもの

enter image description here

したがって、私は抽象Pieceクラスを設計しました。このクラスには、特に、方向に移動できるすべてのセルを追跡するMapオブジェクトがあり、次のようになっていますMap<Direction, LinkedList<Cell>>

さて、クイーンオブクイーン自身のチームがFORWARD方向に攻撃し、chessBoard[6][4]にあるセルに自分自身を置くとします。

イベントのその特定のセルを監視しているQueenまたは他のピースに通知して、各ピースが独自の更新メソッドを呼び出して攻撃の経路を再描画できるようにするための最良の方法は何ですか?

私はすべてのセルがサブジェクトになり、それらを観察している部分だけがオブザーバーになるというオブザーバーパターンで行くことを考えていました。したがって、いずれの場合でも、特定のピースのセットによって監視されているセルのいずれかで何かが発生すると、それらのピースのみが通知されます。

しかし、すべてのセルに最大32のリスナーが必要なため、これが良いアプローチかどうかはわかりません。それがまったくスケーラブルになるかどうかはわかりません。

それを行うためのより良い方法はありますか?

読んでいただきありがとうございます。

16
Auro

「移動可能なすべてのセルを追跡するMapオブジェクト」があると、時期尚早な最適化の例のように見えます。

「ピースが遅くなる可能性がある場合に備えて」、ピースごとにこれらのセルをすべて覚える代わりに、関連するList<Cell>(またはMap<Direction, LinkedList<Cell>>)メソッドが呼び出されたときに計算によって?この方法では、通知やオブザーバーパターン、または「32リスナー」は必要ありません。それが本当に駒の方法なのか、盤の方法なのかは議論の余地があるかもしれません。ただし、このメソッドをピースの代わりにボードに入れると、ピースはボードについて何も知る必要がないため、ボードとピースの間の循環依存を回避できます。

このメソッドが変更されていない同じボードで頻繁に呼び出されてパフォーマンスのボトルネックになると後でわかった場合(これは疑問です)、 memoization を使用して結果をキャッシュできます。これは、Map<Direction, LinkedList<Cell>> 1個あたりが必要ですが、他のボードの変更を積極的に監視する必要はありません。

43
Doc Brown

私の提案は、物事をできるだけ簡単にすることです。オブジェクトに与える情報が多いほど、オブジェクトは複雑になり、追跡が難しくなります。

プログラムをレイヤーに分解してみましょう。そこにあるもの?

  • ピース。彼らは自分がどのような種類の作品で、どのような色(黒または白)かを知っているだけです。
  • ボード。それは、ピースとそれらがどのように配置されているかについてのみ知っています。
  • ゲームの状態。ゲームの状態は、ボードについて、およびキャプチャされたピースなどの無関係な情報についても知っています。
  • UI。 UIはゲームの状態を認識しています。また、スプライトなどのグラフィック情報についても認識しています。

The Pieces。ピースは...単なるピースです。色とタイプ(ルーク、キング、クイーンなど)がありますが、それだけです。それは答えることができます:

  • 私は何でしょう? (ルーク、キング、クイーンなど)
  • どちらに所属しますか? (黒、白)

The Board。ボードは、セルが断片を持つか持たないかの2D配列です。それは答えることができます:

  • ピースはどこにありますか?
  • 空の正方形はどこですか?

さらに、ボードの現在の状態に基づいて、次の計算を依頼できます。

  • 特定の場所で駒が攻撃できるのはどのマスですか?
  • 特定の部分が危険にさらされていますか?
  • 王はチェックしていますか?
  • 提案された移動は合法ですか?

例:移動B3 to A4

  • B3に駒がない場合、それは違法です。
  • ピースが斜めに移動できない場合、それは違法です。
  • A4が同じ色の部分で占められている場合、それは違法です。
  • キングがチェック状態にあり、その動きがキングを保護しない場合、それは違法です。等々。

ゲームの状態。ゲームの状態は、ボードの現在の状態とともに、ゲームでこれまでに起こったことを追跡します。これも:

  • ボードは今どのように見えるか
  • 動きの歴史(「クイーンがB4をとる」のように)
  • 誰がどの部分をキャプチャしたか
  • 誰の番ですか?

I。 Tbe UIは、ゲームの状態とボードの描画を担当します。私たちはそれを尋ねることができます:

  • 物事はどのように見えますか? (どのスプライトまたはグラフィックプリミティブを使用して、ピースまたはボードを描画しますか?)
  • どこに描いたらいいの?
  • いつ絵を描くべきですか?

UIは、イベントを処理する唯一のものでなければなりません。ピースはイベントを処理しません。ゲームの状態はイベントを処理しません。また、理事会はイベントを処理しません。イベントが発生すると、UIは次のことを行います。

  • 起こったことが合法であったかどうかを確認します
  • 物事が合法である場合、ゲームの状態を更新します
  • 更新されたボード(および関連する更新アニメーション)を描画します

可能な移動を強調表示するなど、どうすればよいですか?ユーザーがボード上にマウスを置くと、UI

  • それがどの正方形かを計算する
  • ゲームの状態からボードを取得します
  • その駒がどのマスを攻撃できるかをボードに尋ねます。

この最後の質問は、入力として位置を取り、合法的に攻撃される可能性のある位置のリスト(または配列)を返すボードのメンバー関数の形で実装できます。

このアプローチを使用する理由すべてのフィールド、すべてのプロパティ、クラスに追加するすべてのメンバーは、stateを追加します。すべての状態は、管理し、追跡し、必要に応じて更新する必要があります。コードはめちゃくちゃになり、1つの小さな変更で突然12の異なるクラスを変更する必要があるため、変更は困難です。

ピースの2D配列に基づいて合法的な動きのリストを計算することは、ピースごとにMap<Direction, LinkedList<Cell>>を継続的に更新するよりもはるかに安価です。コードも少なくなります。

プログラムをレイヤーに分割し、各レイヤーに単一の責任(その下のレイヤーを制御する)を与えることにより、クリーンで効率的でモジュール式のプログラムを作成できます。

22

他の回答はすでに、あなたが求めていることをどのように行うことができるかを答えました。ここで指摘したいのは、OOPもデザインパターンも(そうではありませんが、まったく異なるものです)は、それらを使用するためだけに使用するべきではないことです。

これらはツールであるため、到達したい目的に応じて選択する必要があり、その逆ではありません。

チェスプログラムを作成する必要があり、OOPを使用することを選択した場合、またはOOPを学習してから、学習用の導管としてチェスを選択する場合それ。

それが後者である場合、私が疑っているように、私の提案は低いバーから始めることですが。

OOP他の多くと同じようにボードゲームを学ぶのは素晴らしい方法であり、私がこれまで使用してきたほとんどのプログラムよりも優れています。しかし、よりシンプルなゲームでスターを付けることをお勧めします。tic- tac-toe、そしてチェスやさらに複雑なゲームに到達するまで、複雑さのはしご(チェッカー、マインスイーパ、バックギャモンなど)を上に移動します。

あなたの目標がOOPを学ぶことである場合、私が提案しているアプローチは、OOPを介してチェスを行う方法よりも多くの概念と実践を学ぶように導くでしょう。