JavaScript: ボードゲームのクラスを作る (1)
(工事中) 囲碁、将棋などのボードゲームのクラスを作り方の話です。
モデルとビュー・コントローラーを分けて考える
・盤面の駒や石の配置を表すモデル。 ・GUI に盤面を表示するビュー、人間の操作で駒を動かすコントローラ ・たとば、棋譜データの処理や AIのエンジンで盤面の駒を処理する 話では、モデルだけあれば間に合います。 人間用に盤面を表示したりマウス操作やキー操作をしたりする処理は 不要になります。
共通の処理とゲーム固有の処理に分けて考える
各ゲームに共通する処理と、ゲーム固有の処理に分けて考えます。 共通の処理は親のクラスにし、 ゲーム固有の特殊な処理は 親クラスを継承したサブクラスになるように構成します。
+ 親クラス : どのボードゲームにも共通の処理 + 子クラス : ゲームごとに固有の処理 例: ・(親)ボードゲームのクラス + (子) 囲碁のクラス + (子) 将棋のクラス + (子) チェスのクラス など
各ゲームの共通点と相違点を調べてみます。
例: 囲碁、連珠 (五目並べ), オセロ、将棋、チェスなど ◎共通点 ・2次元配列に配置された石や駒を扱う。 石や駒を置く、取り除くなど。 ◎相違点 ・ゲームごとに石の取り方や駒の動かし方のルールが異なる。 囲碁、連珠、オセロ : 石を置く。 将棋、チェス : 盤上の駒を別の場所に動かす。 ◎石を置くゲームの場合 ・囲碁、連珠、オセロなど ・共通点 盤上に石を置く。石を別の場所に動かす話は出てこない。 すでに石が置かれた場所には石を置けない (着手禁止)。 など。 ・相違点 石の取り方が異なる。 連珠 : 石を取る話はない。 オセロ : はさまれた相手の石を自分の石の色に裏返す。 囲碁 : 囲まれた石を取る。 など。 ◎駒を動かすゲームの場合 ・相違点 将棋とチェスでは駒の名前や駒の動かし方が異なる。 成れる駒のルールが異なる。 将棋は取った駒を再び盤上に置けるルールがある。 など。
どのゲームにも共通する処理は親クラスにまとめ、 ゲームごとに異なる部分は親クラスを継承したサブクラスを用意します。
◎クラスの構成例 + Matrix 2次元配列を扱うクラス + BoardModel 囲碁、将棋、オセロなどに共通する処理を扱うクラス + GoModel 囲碁用のモデル + ShogiModel 将棋用のモデル + ChessModel チェス用のモデル など
駒のクラスを用意する
モデル (駒を置く2次元配列) にセットする駒や石の値を考えてみます。 単純なプログラムでは文字列で間に合わせることも多いです。
◎ 2次元配列にセットする値の例 //モデルに駒を表す文字列をセットする let piece = "玉"; Model.set(x, y, piece); セットする文字列の例 囲碁 : "X" (黒石), "O" (白石) ネット対戦で使用する GTP プロトコルの文字。 将棋 : "玉", "v玉" (kifの表記), "S" (銀), "P" (歩) USI の表記 チェス : "K", "k" (キング) UCI の表記。大文字と小文字で白と黒を区別する。
値を文字列にすると単純で分かりやすいですが デメリットもあります。
例: ・文字列の値だとチェックがルーズになりやすい (こちらが重要) 誤ったデータがセットされたときのチェックがしにくい。 unidefined や null のチェックをしたり、無関係な文字列でない かチェックしたりする処理が必要。こういう処理は記述が面倒に なったり、チェックもれが起こりやすい。 ・文字列比較は重い処理になりやすい。(こちらは要らんか) JavaScript で ==, != で比較する処理は型変換や文字列の1文字 ずつの比較で重い処理になりやすいです。 人間が操作する GUI の処理では大した問題はありませんが、 モデルで大量の計算を行うような処理ではデメリットになります。 (補足) JavaScript でこういう話をするのは不毛なのだけど・・・ 大量の計算は C 言語で行う。Java,C# すら使わない。 それは置いておいて・・・。
そこで、モデルにセットする値は文字列にせず、駒のクラスの オブジェクトにします。
+ Piece 駒・石を表すクラス。各ゲームに共通の親クラス + GoPiece 碁石のクラス。白石、黒石 + ShogiPiece 将棋の駒のクラス。玉、金、銀など + ChessPiece チェスの駒のクラス。キング、クイーン、ルークなど ...
例: //将棋の駒のオブジェクトを作る let piece = new ShogiPiece( 引数は省略 ); //駒をモデルにセットする。 Model.set(x, y, piece); ・モデルにセットする値は if (piece instanceof Piece){ ... } でチェックする。オブジェクトの型を見て判定します。 Piece (と Piece のサブクラス) 以外のオブジェクトの場合は false になります。 ・比較処理は !==, === で行う。 != や == 比較は行わない。 オブジェクトが完全に一致するかしないかだけを見ます。 内部的にはオブジェクトのポインタ(メモリ上のオブジェクトのある場所) を 1回比較するだけの数値処理になります。 !=, == のような型変換や文字列の文字を 1文字ずつ調べる 処理はしません。
イミュータブルクラス、イミュータブルオブジェクト
Piece のようなクラスのオブジェクトは、定数オブジェクト あるいは不変オブジェクト (イミュータブル・オブジェクト immutable object) と呼ぶことがあります。 Java や C# でこのような用語がよく使われます。 const の値と同じような感覚でオブジェクトを扱います。