対戦版オセロの完成(闇魔術入門)

インタラクティブ・ゲーム制作
<プログラミングコース>
第9回
対戦版オセロの完成
(闇魔術入門)
授業のリスケジュール
• 最終回に海外出張が
入る可能性大
– SIGGRAPH2013
– 補講はちょっと
難しい……
• 土曜日?
• ニコ生?
• コードレビューの
代わりにKinect体験
• Boostか数学で悩む
9. 対戦版オセロ完成
10.Kinect体験
11.Boost or 数学
12.AIレクチャー
13.オセロAI対決
14.(休講)
今日の内容
• 対戦ツールとしての機能を完成させる
– 挟んで部分がひっくり返れば対戦はできる
– 後は置けない判定&決着判定
• C++の闇魔術をちょこっと体験する
– 演算子再定義(オペレータオーバーロード)
本日のメインディッシュ
置ける場所とひっくり返し判定
置ける場所の定義(1)
• 置くことで
ひっくり返せること
– そのためには、
置く場所の
周囲8マスに
相手の石が
置かれているのが
前提条件になる
– 初手で黒が置けるのは、
前提条件のみだと
右の赤いセル
○ ●
●○
あるセルにおける
周囲の情報を得るには
• cellInfo[x][y]の周囲8セル
– [x-1][y-1], [x][y-1], [x+1][y-1]
– [x-1][y],
[x+1][y]
– [x-1][y+1], [x][y+1], [x+1][y+1]
• 範囲外(0未満、8以上)の場合は対象外
• これをこのままコーディングすると
ださいので、ループで処理しよう
コーディング例
CellPosition
aroundPos;
for(int i = -1; i <= 1; ++i) {
// 横軸を-1から+1まで
aroundPos.x = pos.x + i;
// ずらしたX座標値を得る
// 範囲外だったら処理しない
if(aroundPos.x < 0 || aroundPos.x > 7) continue;
for(int j = -1; j <= 1; ++j) {
// 縦軸を-1から+1まで
// 基準座標の時は処理しない
if(i == 0 && j == 0) continue;
aroundPos.y = pos.y + j;
// ずらしたY座標値を得る
// 範囲外だったら処理しない
if(aroundPos.y < 0 || aroundPos.y > 7) continue;
// 相手の色だったらとりあえず置けそうな場所とする
if(cellInfo[aroundPos.x][aroundPos.y].color == oppositeColor) {
vecList.push_back(CellPosition(i, j));
cout << ": OK" << endl;
} else {
cout << ": NG" << endl;
}
}
}
置ける場所の定義(2)
• 相手の石があった
方向の先に、
自分の石があること
– 端に到達した場合は
その時点でNG
– 隣接する相手の石が
複数存在した場合は、
それぞれの方向で判定
– 初手の場合青いセルが
最終的にOKになる
○ ●
●○
こんな手順で実装した
1. 石を打った位置から、相手の石が
見つかった方向へ1歩ずつ進む
2. 相手の石が見つかった時は、その座標を
一時的にメモしながら更に先へ進む
3. 自分の石が見つかったら、これまでメモ
していた相手の石リストを、正式なリスト
に加えてその方向の探索を終了する
4. 石が置かれてない、盤面外に出た、等の
場合は、これまでのメモを破棄して探索を
終了する
ひっくり返し候補リストができた
• ループでブン回してひっくり返すだけ
• なのだが、これを毎回必ずやってしまう
のはちと問題
– この後のToDoでそれを考えよう
最初から良い設計で書けなくてもしょうがない
リファクタリング
行き当たりばったりコーディング
• 実は私もそう
– 授業見てれば分かるだろ!
• じゃあいつ直すか?
– 「今でしょ」はもう使い古されすぎ
– 『この設計クソやなほんま!』と思った時が
直し時!
• 何かをする時に面倒だと思う設計はよろしくない
• 面倒なままゴリ押すより、設計を見直した方が
一見手間が増えても結果的には良いことが多い
今回のプロジェクトの
大きな変更点
• 盤面サイズの定数化
– 8とか、それに基づく7や35なんて数を
直接ハードコーディングするのはダサすぎる
• Stoneの管理をBoardへ委任
– 石はボードの上に乗っている
– つまりBoardのメンバにするのが自然
• 構造体の追加
– 1つのセルに複数の数値を持たせたい
– 2つの数値のペアを1まとまりに扱いたい
今日のToDo
• 石を置けるかどうかだけをチェックする
処理が必要
– 現状だと石を置けるかチェックし、
ひっくり返すところまでやってしまう
• 勝ち負けを判定するにはどうすれば?
– 盤面が石で埋まったとき
– お互い打てる場所が無くなったとき
闇魔術の初歩の初歩
演算子再定義
(オペレータオーバーロード)
CellPosition構造体で
ちょこっと使用
// 闇魔術:演算子再定義(オペレータオーバーロード)
friend CellPosition operator+
(const CellPosition &l, const CellPosition &r)
{
return CellPosition(l.x + r.x, l.y + r.y);
};
// ↑これを作ることで
// ↓こう書ける
vCheck = vCheck + (*ite);
// 使わない場合はこう書いてた↓
//vCheck.x = vCheck.x + ite->x;
//vCheck.y = vCheck.y + ite->y;
演算子も関数だということ
• 演算子の左右に来る
変数やオブジェクトを引数として取り、
演算結果をreturnするだけのもの
• 自分の独自ルールが作れるが、
あまりに分かりづらいルールは
共同開発ではやめよう
– +演算子なのに減算される
– =演算子なのに代入にならない
TO BE CONTINUED…