Undefined

この記事は1年以上前に書かれたものです。現在の状況にそぐわない場合がございますのでご注意ください。

JavaScriptで三目並べゲームをつくる 第3回

進行管理クラスオブジェクトを実装する

この記事について

今回は前回の記事でご紹介したクラスモジュールを使用して、進行管理クラスオブジェクトの実装をご紹介したいと思います。

三目並べゲームの完成形はこちらです。

クラスオブジェクト

クラスの概要

ゲームの進行全般を受け持つチームリーダー的なクラスです。マス目が選択されるなどゲーム中に何らかの動きがあったときはこのクラスに通知され、このクラスから必要に応じて各クラスに仕事の依頼が通知されることになります。

どんな情報を受け持つ? (プロパティ) どんな仕事を受け持つ? (メソッド)
  • 参加プレイヤー数
  • 現在のゲーム状態
  • 終了状態(勝利者あり・引き分け)
  • 現在何手目か
  • 現在のプレイヤー
  • 勝利したプレイヤー
  • ゲームを(リ)スタートする準備をする
  • ターンを更新して次のプレイヤーを決定する
  • ゲーム状態に応じて表示更新を通知する
  • 勝利者を決定する
  • ゲーム終了を監視する(勝利、引き分け)

プライベートな静的変数(定数)

// ゲームの画面状態
var SCREEN_STATE = {
  start  : 'start',
  restart: 'restart'
};

// ゲームの進行状態
var ACTION_STATE = {
  empty: -1,
  draw :  0,
  win  :  1,
  next :  2
};

ゲーム進行全体に関わるふたつの静的変数を持っています。

  1. SCREEN_STATEはゲームプレイ状態の特定に使用します。
    • ゲームスタート画面・プレイ画面start
    • ゲーム結果(リスタート)画面中restart
  2. ACTION_STATEはゲームが続行中かどうかやゲーム結果の特定に使用します。
    • 下記状態以外の場合はempty
    • ゲーム終了時、結果が引き分けの場合はdraw
    • ゲーム終了時、勝利者がいる場合はwin
    • ゲーム続行時、次ターンに進む場合はnext

SCREEN_STATEの特定方法は直感的によく分からない分け方で大失敗です…

コンストラクタ

/* コンストラクタ */
var that = function GameControl(options) {
  : 
}

このインスタンスオブジェクトを生成するところからゲームの処理が始まります。引数optionsはゲーム設定オプションです。

ゲームを実行開始する箇所はこんな感じになっています。

// ゲーム設定オプション
var options = {
  board: {
    num: 3 // 何目並べか
  },
  players: [{
    name : 'プレイヤー1', // プレイヤー表示名
    color: '#d43c32', // マス目の色
    auto : false       // オートプレイかどうか
  }
  , {
    name : 'プレイヤー2',
    color: '#decd04',
    auto : true
  }]
};

// GameControlクラスオブジェクトコンストラクタに引数を渡します。
GAME.gameControl = new GAME.GameControl(options);
// スタート画面表示
GAME.gameControl.scene();

ゲーム設定オプションはオブジェクトリテラルです。コンストラクタに引数を渡すことでプレイ人数や何目並べかを決めるようにしました。

どんな情報を持っているか (プロパティ)

/* プロパティー */
// 設定
this.options;
// プレイヤー
this.players = [];
// プレイヤー数
this.playerNum;
// 現在のゲーム状態
this.state;
// 終了状態
this.actionStat;
// 何手目か
this.turnNum;
// 現在のプレイヤー
this.curPlayer;
// 勝利したプレイヤー
this.winner;
// カスタムイベント
this.ev = {};

/* DOMアクセス */
// ゲームエリア
this.gameEl = document.querySelector('#js-game');

進行管理クラスオブジェクトが受け持つ情報はゲーム全体の進行に関わるものです。各プロパティが保持する情報はコメントの通りでっす!

this.gameElはゲームボードのDOM要素と情報エリアのDOM要素(つまりゲームに関わるエリアの全体)を覆っているDOM要素を保持します。
イベントの通知にはイベントの受け手がいずれかの要素と紐づいている必要があるので、一番適切と思われるこのDOM要素を利用しています。

どんな通知を受け取るか(イベント)

/* カスタムイベント作成 */
// スタートボタンがクリックされたとき
this.ev.started = document.createEvent('Event');
this.ev.started.initEvent('gameControl.started', false, true);
// マス目が更新されたとき
this.ev.selected = document.createEvent('Event');
this.ev.selected.initEvent('gameControl.selected', false, true);

/* イベントリスナー設定 */
// スタートボタンがクリックされたとき
this.gameEl.addEventListener('gameControl.started', this.started.bind(this), false);
// マス目が更新されたとき
this.gameEl.addEventListener('gameControl.selected', this.selected.bind(this), false);

ふたつのカスタムイベントを使用しています。

  1. イベントタイプgameControl.startedは、モーダルウィンドウ内の(リ)スタートボタンがクリックされたとき発生します。
  2. イベントタイプgameControl.selectedは、ゲームボードのマス目がクリックされたとき発生します。

カスタムイベントは以下の手順で登録します。こちらはイベントの受け手側の準備であり、イベントを準備したターゲットの要素に対していずれかの場所でdispatchEventを使いイベントを呼び出すことになります。

  1. createEventイベントオブジェクトを作成
  2. initEventで作成したイベントオブジェクトを初期化、イベントタイプを設定
  3. addEventListenerで設定したイベントタイプに、ターゲットの要素とイベントが発生した時のイベントリスナーを登録します。

ゲーム読み込み時の初期設定

/* プロパティー設定 */
// コンストラクタの引数で渡されたゲーム設定
this.options = options;
// 現在のゲーム状態をスタートにする
this.setState(SCREEN_STATE.start, -1);

/* インスタンス生成 */
// モーダルウィンドウインスタンスを生成
GAME.modal = new GAME.Modal(this.state, this.actionState);

インスタンスオブジェクトが生成されるときの処理です。モーダルウィンドウクラスオブジェクトからモーダルウィンドウインスタンスを生成することで、スタート画面を表示します。

どんな仕事道具を持っているか
(進行管理についてのメソッド)

このクラスはゲームの進行管理に使う12個のメソッドを持っています。

sceneメソッド

/**
 * scene
 * 画面を変更する
 */
scene: function() {
  // モーダルウィンドウに通知
  GAME.modal.modalEl.dispatchEvent(GAME.modal.ev.update);
},

モーダルウィンドウインスタンスで定義されているイベントオブジェクトGAME.modal.ev.updateにモーダルウィンドウの表示を依頼(通知)します。表示画面はスタート画面かリスタート(結果)画面でstateプロパティにより決められます。

startedメソッド

/**
 * started
 * ゲームスタート
 */
started: function() {
  // スタート時のみの処理
  if(SCREEN_STATE.start === this.state) {
    /* インスタンス生成 */
    this.createInstances();
  }

  // ゲームを始められる状態にセットする
  this.reset();

  // 現在の画面の状態、終了状態をセット
  this.setState(SCREEN_STATE.start, ACTION_STATE.next);

  // プレイヤーに通知
  for (var i = 0; i < this.playerNum; i++) {
    this.players[i].playerEl.dispatchEvent(this.players[i].ev.reset);
  }

  /* 通知 */
  // ゲームボードに通知する
  GAME.board.boardEl.dispatchEvent(GAME.board.ev.init);

  // 情報エリア要素に通知する
  GAME.info.infoAreaEl.dispatchEvent(GAME.info.ev.update);

  // 現在のプレイヤーインスタンスに通知する
  var curPlayer = this.players[this.curPlayer];
  curPlayer.playerEl.dispatchEvent(curPlayer.ev.turn);
},

ゲームスタートの準備をしてゲームのプレイを始めるためのメソッドです。(リ)スタートボタンがクリックされたときに実行されます。

9行目:初回のスタート時のみゲームボード、情報エリア、プレイヤーの各インスタンスを作成しています。次回(リスタート)からは作成された各インスタンスの持つ状態のみをリセットしてリプレイすることになります。

32行目:メソッドの最後にプレイヤーインスタンスにターン開始を通知してゲームのプレイが始まります。

endedメソッド

/**
 * ended
 * ゲームが終了したときの後処理
 *
 * @param number resultState 終了状態
 */
ended: function(resultState) {
  // 現在の画面の状態、終了状態をセット
  this.setState(SCREEN_STATE.restart, resultState);

  //リスタート(結果)画面を表示する
  this.scene();
},

リスタート(結果)画面を表示するメソッドです。ゲームプレイ終了時に実行されます。

9行目:画面の状態stateをリスタートSCREEN_STATE.restartにして、ゲーム結果actionStateを、引き分けACTION_STATE.drawか勝利者ありACTION_STATE.winにします。

12行目:リスタート(結果)画面を表示します。

selectedメソッド

/**
 * selected
 * マス目が選択されたときの処理
 */
selected: function() {
  // 現在のプレイヤーインスタンスに通知する
  var curPlayer = this.players[this.curPlayer];
  curPlayer.playerEl.dispatchEvent(curPlayer.ev.add);

  // 次にすべき行動を決定する
  this.nextAction(curPlayer);
},

ゲームプレイ時にマス目が選択されたときに実行されます。マス目選択時の状態を更新したのち、ゲームの進行を決定します。マス目の状態や表示の更新はマス目が選択された直後にこのメソッドが実行されます。

7~8行目:現在のプレイヤーに状態更新依頼を通知します。実際には選択したマス目情報を更新します。

11行目:現在のゲーム進行状態から次のゲーム進行を決定します。引数のcurPlayerは次のゲーム進行が勝利者ありの場合に勝利者の特定に使います。

ゲームの進行状態 次のゲーム進行アクション
勝利者あり リスタート(結果)画面へ
引き分け リスタート(結果)画面へ
次のターン  次のターンへ

resetメソッド

/**
 * reset
 * ゲームを始められる状態にする
 */
reset: function() {
  this.turnNum = 1; // 何手目か
  this.curPlayer = 0; // 現在のプレイヤー
  this.winner = null; // 勝利したプレイヤー
},

ゲームプレイ開始時にときに実行されます。ゲームプレイのための状態を初期化しています。

setStateメソッド

/**
 * setState
 * ゲームの状態をセットする
 * 
 * @param string state 現在のゲーム状態
 * @param string actionState 終了状態
 */
setState: function(state, actionState) {
  // 現在の画面の状態
  this.state       = state;
  // 終了状態
  this.actionState = actionState;
},

ゲームの状態を管理するメソッドです。プレイスタート時、プレイ終了時に実行されます。プレイ終了にはthis.actionStateをもとに結果を表示します。

createInstancesメソッド

/**
 * createInstances
 * インスタンスを生成する
 */
createInstances: function() {
  // ゲームボードインスタンスを生成
  GAME.board = new GAME.Board(this.options.board.num);

  // 情報エリアクラスインスタンスを生成
  GAME.info = new GAME.Info();

  // プレイヤー数
  this.playerNum = this.options.players.length;

  // プレイヤーインスタンスを生成
  for (var i = 0; i < this.playerNum; i++) {
    this.players.push(new GAME.Player(this.options.players[i]));
  }
},

ゲームに必要な各インスタンスを生成するメソッドです。ゲームプレイ初回のプレイ開始時に実行されます。ゲームボードコンストラクタには何目並べかの情報this.options.board.numが渡されます。また、プレイヤーインスタンスは参加人数this.playerNumの数だけ(オートプレイヤー含む)生成されます。

nextActionメソッド

/**
 * nextAction
 * 次にすべき行動を実行する
 *
 * @param object curPlayer 現在のプレイヤー
 */
nextAction: function(curPlayer) {
  switch (this.checkNextAction(curPlayer)) {
    // 勝利者あり
    case ACTION_STATE.win:
      this.winner = curPlayer; // 勝利者
      this.ended(ACTION_STATE.win);
      break;
    // 引き分け 
    case ACTION_STATE.draw:
      this.ended(ACTION_STATE.draw);
      break;
    // 次のターン
    case ACTION_STATE.next:
      this.nextTurn();
      break;
  }
},

次のゲーム進行を実行するメソッドです。ゲームプレイ時にマス目が選択されたときに実行されます。

11行目:勝利者ありの場合のみthis.winnerに現在のプレイヤー(勝利者)をセットします。勝利者情報は結果画面表示の際に使います。

ゲームの進行状態 次のゲーム進行アクション
勝利者あり ACTION_STATE.win リスタート(結果)画面へ
引き分け ACTION_STATE.draw リスタート(結果)画面へ
次のターン ACTION_STATE.next  次のターンへ

checkNextActionメソッド

/**
 * checkNextAction
 * 次の行動を決定する
 *
 * @param object curPlayer 現在のプレイヤー
 * @return string 次の行動
 */
checkNextAction: function(curPlayer) {
  var result;

  // 勝敗のつく最小ターン以下
  if (this.isNoCount(GAME.board.unitNum)) {
    result = ACTION_STATE.next;
  // 勝利者あり
  } else if(this.isWinner(curPlayer)) {
    result = ACTION_STATE.win;
  // 引き分け
  } else if (GAME.board.allUnitNum <= this.turnNum) {
    result = ACTION_STATE.draw;
  // 次のターン
  } else {
    result = ACTION_STATE.next;
  }

  return result;
},

次のゲーム進行を決定するメソッドです。次のゲーム進行を実行するときに実行されます。以下のうちのいずれかを返します。

条件 ゲームの進行状態
  • 勝利者がいる
勝利者あり ACTION_STATE.win
  • 引き分け(全マス目が選択されている)
引き分け ACTION_STATE.draw
  • 勝敗のつく最小ターン以下
  • 勝利者がいない
  • 引き分け(全マス目が選択されていない)
次のターン ACTION_STATE.next

nextTurnメソッド

/**
 * nextTurn
 * ターンを先に進める 
 */
nextTurn: function() {
  var curPlayer;

  // プレイヤーを次に進める
  this.curPlayer = this.turnNum % this.playerNum;

  // ターン回数を更新する
  this.turnNum++;

  // 情報エリア要素に通知する 
  GAME.info.infoAreaEl.dispatchEvent(GAME.info.ev.update);

  // 現在のプレイヤーインスタンスに通知する
  curPlayer = this.players[this.curPlayer];
  curPlayer.playerEl.dispatchEvent(curPlayer.ev.turn);
},

ターンをひとつ先に更新するメソッドです。ゲーム進行アクションが次ターンACTION_STATE.nextのとき実行されます。

9,18〜19行目:現在のターン数をプレイヤー数で割った余りの数を、参加プレイヤーオブジェクトを管理するプロパティthis.playersの配列のインデックスに利用して、次のターンのプレイヤーインスタンスオブジェクトに通知しています。

15行目:情報エリアインスタンスオブジェクトに表示更新依頼を通知しています。(『◯◯さんのターン』の表示部分)

isWinnerメソッド

/**
 * isWinner
 * 現在のプレイヤーが勝利しているかどうか
 *
 * @param object curPlayer 現在のプレイヤー
 * @return boolean 勝利者かどうか
 */
isWinner: function(curPlayer) {
  var unitNum   = GAME.board.unitNum, // 一辺のマス目数
      selectNum = GAME.board.curSelect, // 選択されたマス目
      condition; // 選択されたマス目に紐づく勝利条件

  // 勝利条件を取得する
  condition = this.setCondition(selectNum, unitNum);

  // 勝利条件と現在のプレイヤーの持ちマス目を比較する
  if(this.compareOwnUnitsWithConditon(condition, curPlayer.ownUnits)) {
    return true;
  }
  
  return false;
},

現在のプレイヤーが勝利条件を満たしているかチェックするメソッドです。次のゲーム進行を決定するときに実行されます。

14行目:選択されたマス目に紐づく勝利条件をマス目のインデックスの二次元配列で取得しています。勝利条件は以下の4パターンになります。

勝利条件パターン

  1. 縦並びの勝利条件
  2. 横並びの勝利条件
  3. 斜め(右上がり)の勝利条件
  4. 斜め(右下がり)の勝利条件

17〜21行目:現在のプレイヤーがいままでに選択したマス目と勝利条件を比較することで勝利条件を満たしているかどうかチェックして満たしている場合のみtrueを返します。

isNoCountメソッド

/**
 * isNoCount
 * 勝敗がつく最小ターン以下かどうか
 *
 * @param number unitNum 一辺のマス目数
 * @return boolean 最小ターン以下かどうか
 */
isNoCount: function(unitNum) {
  var noCount = (unitNum - 1) * this.playerNum;

  if ( noCount >= this.turnNum ) {
    return true;
  }

  return false;
},

勝敗がつく最小ターン以下かどうかチェックするメソッドです。次のゲーム進行を決定するときに実行されます。

9行目:勝敗がつく最小ターンは、一番手のプレイヤーが一辺のマス目を並べるために必要なターン数になるはずですので、一辺のマス目数から1を引いた数(リーチがかかるマス目数)にプレイヤー数を掛けた数が、勝敗のつく可能性のあるターンにギリギリ届かないターン数になるかと思います。

( 一辺のマス目数 - 1 ) * プレイヤー数

11〜13行目:この数を現在のターン数と比較して同じか小さい場合は勝敗がつく最小ターン以下をあらわすtrueを返すことで、無駄な勝利判定を無くしています。

どんな仕事道具を持っているか
(勝利条件についてのメソッド)

勝利条件に使用する6つのメソッドを持っています。これらのメソッドはゲームを進行するにあたって必要となる実体を持たない抽象的な一連のルールの集まりです。ですので、ルールを管理するひとつの役割の集まりとして別クラスオブジェクトに分けてしまうのもありかもしれませんね。

setConditionメソッド

/**
 * setCondition
 * 選択したマス目から勝利条件を設定する
 *
 * @param number selected 選択されたマス目
 * @param number side 一辺のマス目数
 * @return array 勝利条件
 */
setCondition: function(selected, side) {
  var vertical, horizontal, slantingUp, slantingDown, result = [];

  vertical     = this.setVerticalCondition(selected, side);
  result.push(vertical);

  horizontal   = this.setHorizontalCondition(selected, side);
  result.push(horizontal);

  slantingUp   = this.setSlantingUpCondition(selected, side);
  result.push(slantingUp);

  slantingDown = this.setSlantingDownCondition(selected, side);
  result.push(slantingDown);

  return result;
},

選択したマス目から考えられる勝利条件を設定するメソッドです。現在のプレイヤーが勝利条件を満たしているかチェックするときに実行されます。

12〜22行目:選択されたマス目に紐づく勝利条件をマス目のインデックスの二次元配列で取得しています。勝利条件は以下の4パターンになります。

勝利条件パターン

  1. 縦並びの勝利条件
  2. 横並びの勝利条件
  3. 斜め(右上がり)の勝利条件
  4. 斜め(右下がり)の勝利条件

 斜め系については該当する(斜め並びで勝つ)マス目ではないときは空の配列が返ってきます。

ゲームボードのインデックス番号対応表(三目並べの場合)

ゲームボードのマス目のインデックス番号対応図です

例えば、三目並べで右上のマス目を選択した場合の勝利条件はこのようになります。

result = [
  [2, 5, 8], // 縦並びの勝利条件
  [0, 1, 2], // 横並びの勝利条件
  [2, 4, 6], // 斜め(右上がり)の勝利条件
  [] // 斜め(右下がり)の勝利条件(勝利条件なし)
]

setSlantingUpConditionメソッド

/**
 * setSlantingUpCondition
 * 斜め(右上がり)の勝利条件を設定する
 *
 * @param number selected 選択されたマス目
 * @param number side 一辺のマス目数
 * @return array 勝利条件
 */
setSlantingUpCondition: function(selected, side) {
  var vertical   = Math.floor(selected / side),
      horizontal = selected % side, // 選択されたマス目の縦のインデックス
      ind        = side - 1, // 一辺のもつ最大インデックス番号
      below      = ind - horizontal, // 選択されたマス目より下の行数
      above      = ind - below, // 選択されたマス目より上の行数
      result     = [];

  // 該当のマス目じゃないとき
  if (vertical !== (ind - horizontal)) { return result; }

  // 選択されたマス目
  result.push(selected);

  // 選択されたマス目より下の勝利条件インデックス番号
  for (var i = 1; i <= below; i++) {
    var belowInd = selected - ((side - 1) * i);
    result.push(belowInd);
  }

  // 選択されたマス目より上の勝利条件インデックス番号
  for (var i = 1; i <= above; i++) {
    var aboveInd = selected + ((side - 1) * i);
    result.push(aboveInd);
  }

  // 勝利条件のマス目のインデックス番号を昇順にソートする
  this.sort(result);
  return result;
},

斜め(右上がり)並びの勝利条件を設定するメソッドです。現在のプレイヤーが勝利条件を満たしているかチェックするときに実行されます。

斜め(右上がり)並びの勝利条件が必要ない(成り立たない)場合は空の配列[]を返します。

setSlantingDownConditionメソッド

/**
 * setSlantingDownCondition
 * 斜め(右下がり)の勝利条件を設定する
 *
 * @param number selected 選択されたマス目
 * @param number side 一辺のマス目数
 * @return array 勝利条件
 */
setSlantingDownCondition: function(selected, side) {
  var vertical   = Math.floor(selected / side), // 選択されたマス目の縦のインデックス
      horizontal = selected % side, // 選択されたマス目の横のインデックス
      ind        = side - 1, // 一辺のもつ最大インデックス番号
      below      = ind - horizontal, // 選択されたマス目より下の行数
      above      = ind - below, // 選択されたマス目より上の行数
      result     = [];

  // 該当のマス目じゃないとき
  if (vertical !== horizontal) { return result; }

  // 選択されたマス目
  result.push(selected);

  // 選択されたマス目より下の勝利条件インデックス番号
  for (var i = 1; i <= below; i++) {
    var belowInd = selected + ((side + 1) * i);
    result.push(belowInd);
  }

  // 選択されたマス目より上の勝利条件インデックス番号
  for (var i = 1; i <= above; i++) {
    var aboveInd = selected - ((side + 1) * i);
    result.push(aboveInd);
  }

  // 勝利条件のマス目のインデックス番号を昇順にソートする
  this.sort(result);
  return result;
},

斜め(右下がり)並びの勝利条件を設定するメソッドです。現在のプレイヤーが勝利条件を満たしているかチェックするときに実行されます。

斜め(右下がり)並びの勝利条件が必要ない(成り立たない)場合は空の配列[]を返します。

setHorizontalConditionメソッド

/**
 * setHorizontalCondition
 * 横の勝利条件を設定する
 *
 * @param number selected 選択されたマス目
 * @param number side 一辺のマス目数
 * @return array 勝利条件
 */
setHorizontalCondition: function(selected, side) {
  var horizontal = selected % side, // 選択されたマス目の横のインデックス
      ind        = side - 1, // 一辺のもつ最大インデックス番号
      below      = ind - horizontal, // 選択されたマス目より下の行数
      above      = ind - below, // 選択されたマス目より上の行数
      result     = [];

  // 選択されたマス目
  result.push(selected);

  // 選択されたマス目より下の勝利条件インデックス番号
  for (var i = 1; i <= below; i++) {
    var belowInd = selected + i;
    result.push(belowInd);
  }

  // 選択されたマス目より上の勝利条件インデックス番号
  for (var i = 1; i <= above; i++) {
    var aboveInd = selected - i;
    result.push(aboveInd);
  }

  // 勝利条件のマス目のインデックス番号を昇順にソートする
  this.sort(result);
  return result;
},

横並びの勝利条件を設定するメソッドです。現在のプレイヤーが勝利条件を満たしているかチェックするときに実行されます。

setVerticalConditionメソッド

/**
 * setVerticalCondition
 * 縦の勝利条件を設定する
 *
 * @param number selected 選択されたマス目
 * @param number side 一辺のマス目数
 * @return array 勝利条件
 */
setVerticalCondition: function(selected, side) {
  var vertical = Math.floor(selected / side), // 選択されたマス目の縦のインデックス
      ind      = side - 1, // 一辺のもつ最大インデックス番号
      below    = ind - vertical, // 選択されたマス目より下の行数
      above    = ind - below, // 選択されたマス目より上の行数
      result   = [];

  // 選択されたマス目
  result.push(selected);

  // 選択されたマス目より下の勝利条件インデックス番号
  for (var i = 1; i <= below; i++) {
    var belowInd = selected + side * i;
    result.push(belowInd);
  }

  // 選択されたマス目より上の勝利条件インデックス番号
  for (var i = 1; i <= above; i++) {
    var aboveInd = selected - side * i;
    result.push(aboveInd);
  }

  // 勝利条件のマス目のインデックス番号を昇順にソートする
  this.sort(result);
  return result;
},

縦並びの勝利条件を設定するメソッドです。現在のプレイヤーが勝利条件を満たしているかチェックするときに実行されます。

compareOwnUnitsWithConditonメソッド

/**
 * compareOwnUnitsWithConditon
 * 現在のプレイヤーの持ちマス目が勝利条件を満たしているか確認する
 *
 * @param array condition 勝利条件
 * @param array ownUnits 現在のプレイヤーの持ちマス目
 * @return boolean 現在のプレイヤーが勝利条件を満たしているか
 */
compareOwnUnitsWithConditon: function(condition, ownUnits) {
  var cond = condition.filter(function (cond) {
    return Array.isArray(cond);
  });

  // 勝利条件のライン
  for (var i = 0, l = cond.length; i < l; i++) {
    // 勝利条件の各マス目
    for (var j = 0, ll = cond[i].length; i < l; j++) {
      var result = (-1 < ownUnits.indexOf(cond[i][j]));

      // 勝利条件のマス目を持ってない
      if (!result) { break; }

      // 勝利条件のマス目を全て持っている
      // (勝利条件のマス目のループが最後まで走りきることができたら)
      if (j + 1 === ll) { return true; }
    }
  }

  return false;
},

現在のプレイヤーの持ちマス目が勝利条件を満たしているか確認するメソッドです。

15〜27行目:メソッドに渡される2つの引数、勝利条件の配列conditionと、現在のプレイヤーの持ちマス目の配列ownUnitsを比較することで勝利条件を満たしているかチェックします。勝利条件の配列をループの主軸として、各勝利条件のパターン内のマス目ごとにプレイヤーの持ちマス目と比較しています。(18行目)
もし、プレイヤーの持ちマス目の配列が、勝利条件のパターンに含まれているマス目を持っていなかったら即、次のパターンとの比較に移ります。(21行目)
勝利条件のパターンが持つマス目分ループが回りきることができたときは、その勝利条件のパターンを満たしているということになりますのでtrueを返します。(25行目)

sortメソッド

/**
 * sort
 * 数値として昇順にソートする
 *
 * @param array result ソートする数値
 */
sort: function(result) {
  if (!Array.isArray(result) || !result.length) { return result; }

  result.sort(function(a, b) {
    return a - b;
  });
}

数値として昇順にソートするときに使うヘルパーメソッドです。各勝利条件のパターンを設定するときにマス目のインデックスを小さい順にしているのですが、実際はソートする必要はありません


ふー、疲れた…お読みいただきありがとうございました!次回はプレイヤークラスオブジェクトを実装していきたいと思います。

to top