[[TopPage]] > 最終課題に向けて > 演習4-2 オセロの基礎2 * オセロの基礎2 [#rc5dd636] ** コマを置ける条件 [#z3f06a7b] - オセロのコマを置ける条件についてまとめてみます. + 自分のターンである + 置く場所にコマがない + &color(red){置くと,必ず相手のコマが1コ以上裏返る}; - 条件1は[[演習4-1>演習4-1 オセロの基礎1]]で,できました.やったー. - 条件2も,押したボタンがboardIconかどうかを判断するだけなので,すぐできますね.追加しましょう. - 条件3のプログラムは,色々なアルゴリズムが考えられると思います. -- 難しいと思う人は,ここでその一例をご紹介します. -- できそうな部分で参考をやめて,自分で書いていくのが良いですよ. ** 課題4・隣8方向の判定 [#n4f1df21] - それでは,課題4・課題5を通して,条件3を追加してみましょう. -- 押されたボタンがboardIconだと分かった後に,theArrayIndexをint型に変換します. -- その数値をy成分とx成分に分解し([[演習4-1>演習4-1 オセロの基礎1]]参照),以下のように書いてください. if(judgeButton(y, x)){ //置ける PLACE命令の送信 } else { //置けない System.out.println("そこには配置できません"); } - judgeButtonは,これから新しく作る関数の名前です. |&size(14){関数 ''judgeButton''};| |裏返りの発生するボタンであるかどうかを判定する| |%%%引数1%%% y ボタンのy位置(0-7)&br;%%%引数2%%% x ボタンのx位置(0-7)| |%%%返り値%%% 判定(TF)| -- この関数の返り値はBoolean型です.指定した位置について,コマを置けたらtrue,置けないならfalseを返す関数です. - では,mouseMovedの下に,新しい関数を追加します. public boolean judgeButton(int y, int x){ boolean flag = false; //色々な条件からflagをtrueにするか判断する return flag; } - flagをtrueにする手順は複雑ですが,まずはこのような条件を決めてみましょう. ++ 隣8方向に相手のコマがあるか,ひとつずつ見ていく ++ ひとつでもあれば,flagをtrueにする - ここで,隣8方向の参照のしかたを下図に示します. #ref(direction.png,nolink,center,75%)~ CENTER:図1. 隣8方向の求め方 - buttonArray[y][x]を用いると,クリックされたボタンの情報を取得できます. -- このy,xをそれぞれ±1することで,周りのボタンの情報も取ることができますね. - 効率の良いプログラムを作成するには,指定の位置を(0,0)とおき,iとjを用いた相対的な座標を考えます. -- iとjの''二重for文''から,%%%自分(j=0,i=0)以外の%%%まわり8コマを順に見ていきます. -- for文の中で,buttonArray[y+j][x+i]のアイコンがyourIconならばflagをtrueにします. - この段階で,メッセージを出しながらデバッグ(テスト)を行ってください. -- 相手のコマがまわりのどこかにある時に,自分のコマが置けるようになっていますか? - なお,端っこのボタンにコマを置こうとすると,エラーが発生すると思います. -- 8方向の範囲に場外を含むことで,配列が-1や8を参照してしまうためです. -- この問題は,あとで対応するとします. ** 課題5・ひっくり返す判定 [#x140b7ee] - いよいよ,挟まれた相手のコマをひっくり返す判定に移ります. - 課題4の「アイコンがyourIconなら」という条件を,「flipButtons()の返り値が1以上なら」という条件に変えてください. - flipButtonsは今から作る新しい関数です.仮引数にy,x,j,iの4つを与えましょう. |&size(14){関数 ''flipButtons''};| |一方向にあるコマ群を裏返す命令を送る| |%%%引数1%%% y ボタンのy位置(0-7)&br;%%%引数2%%% x ボタンのx位置(0-7)&br;%%%引数3%%% j y成分ベクトル(-1,0,1)&br;%%%引数4%%% i x成分ベクトル(-1,0,1)| |%%%返り値%%% 裏返ったコマの数| -- 返り値はint型です.この関数は,指定した位置と方向について,ひっくり返せるコマの個数を返します. -- flipButtonsの中身では,flagのような返り値用の変数flipNum = 0;も用意しておきます. #br - さて,相手のコマが何個ひっくり返るかを見るには,%%%同じ方向に何個連鎖(連続)して%%%ひっくり返るかを調べなければなりません. -- flipButtonsには,基準となるボタンの絶対位置(y,x)と,ひっくり返る可能性のある方向成分(j,i)を渡します. -- これらの情報があれば,''どこからどの方向に調べていくか''が分かりますね. #ref(flipothello.png,nolink,center)~ CENTER:図2. 隣8方向の先の求め方 - 隣2マス以上に離れたボタンを調べるには,j,iの値を使います.%%%j,iを,方向の単位ベクトルとして用いましょう%%%. -- (1, 0)→(2, 0)→(3, 0)… -- (-1,-1)→(-2, -2)→(-3, -3)… -- 上記を見ると,どの方向も,%%%j,iを足し続ける%%%ことで遠いボタンの位置を把握できそうです. #br - それでは,変数dy,dxを用意して,固定値j,iを足し続けるようなループ文を作りましょう. -- y+dyの値が,調べたいボタンの絶対位置になるようにします. - というわけで,少し変わったfor文の書き方をしてみます.どんな処理か考えてください. for(int dy=j, dx=i; ; dy+=j, dx+=i) { ... } - わかりました? -- for文は,「初期化式; 継続条件式; 再初期化式」の順に書きます. -- 「2つの変数について初期化できる点」「これらの式は省略できる点」がポイントです. -- ただし,このままでは永遠に継続してしまうので,どこかでbreak文を使う必要があります. - このfor文の中身は,以下のようになっていればOKです. ++ &color(red){y+dy,x+dxの位置が場外なら,この関数は0を返す(判定終了)}; ++ この位置のアイコンを取得する ++ アイコンの種類を見る +++ &color(red){boardIconなら,この関数は0を返す(判定終了)}; +++ &color(red){myIconなら,この関数はflipNumを返す(連鎖ストップ)}; +++ &color(red){yourIconなら,flipNumを1増やす(連鎖が続く)}; - ちょっと説明します. -- 1方向に突き進んでいき,連続して相手のコマがある限りは,まだ裏返せる可能性がありますよね.(flipNumを増やし続ける) -- その道中,自分のコマにぶつかったら,「return flipNum」でさっさとfor文どころか関数を抜けます. -- ただし,その道中にboardIconがあったり,自分のコマが見つからないまま場外にはみ出たりした場合も,さっさと0を返しましょう. #br - 実際のひっくり返す命令は,%%%flipNumが1以上%%%の時に,以下のようなループ文を回して書いていきます. for(int dy=j, dx=i, k=0; k<flipNum; k++, dx+=j, dy+=i){ //ボタンの位置情報を作る int msgy = y + dy; int msgx = x + dx; int theArrayIndex = msgy*8 + msgx; //サーバに情報を送る String msg = "FLIP"+" "+theArrayIndex+" "+myColor; out.println(msg); out.flush(); } -- judgeButton,flipButtons内のどちらかに追記しましょう.後者の場合は,break文を利用しましょう. -- judgeButton,flipButtons内のどちらかに追記しましょう.後者の場合は,連鎖ストップ時にbreak文で抜けます. - あとは,FLIP命令の受信部分を,PLACEのように書くだけです. ** 課題6・ゲームの終了と勝敗判定 [#v4015e20] - オセロの公式ルールでは,自分のターンで必ず相手のコマをひっくり返します. -- どこに置いてもひっくり返せない場合に,ターンのパスができます.パスに回数制限はありません. -- ゲームの終了条件は,全てにコマが埋まるか,%%%両者とも置けなくなった%%%場合です. - %%%自動でパスを発生させるか,いつでもパスができるボタンを作るか%%%.どちらか選んでください. #br - 「これ以上コマを置けなくなった」の自動判定は,PLACEするたびに各boardIconのボタンの状況を探りましょう. -- 両者とも置けなくなった時点で,各色のコマ数を数えて勝敗を決めます. - パスボタンを作る場合,本課題では特別に,「お互いにパスが連続したら勝敗の判定に移る」というルールを加えてもOKとします. -- ただし,「全てのコマが埋まったらすぐに勝敗判定に移る」機能は付けてくださいね. - 最後に,ボタンのマウスリスナーを削除して,クリックを無効化する方法を教えます. MouseListener[] listeners = button.getMouseListeners(); for (MouseListener listener : listeners) { button.removeMouseListener(listener); } -- ちょっと面倒!と感じたら,上記の関数化にチャレンジしてみてくださいね. ** おわりに [#kb41a157] - ここまでできたら,とりあえず提出できる''自動オセロ''とみなします! -- 提出作品とする場合は,必ず%%%画像を自分で用意したものにしてください%%%. -- ここから新機能を追加したり,ここで得た知識からほかのゲームの作成を目指せば,高得点につながります(たぶん). -- そのほか,詳細の[[最終課題]]ページをよく読んで,余裕のある課題作りを心掛けましょう! #br &size(16){[[&ref(http://yoslab.net/netprog/next.gif,nolink); 最終課題へ>最終課題]]};