基本情報技術者 2010年 春期 午後 問11
次のJavaプログラムの説明及びプログラムを読んで、設問1,2に答えよ。
〔プログラムの説明〕
リバーシゲームで2人のプレーヤの対戦を支援するプログラムである。
リバーシは、8行8列の升からなる盤に、2人のプレーヤが、一方の面が白、他方の面が黒の石を置いていき、盤上の石の個数を競うゲームである。各プレーヤは自分の石の色(白又は黒)をもつ。盤には、初期状態として、図1のとおり中央に白黒2個ずつの石を配置する。升の位置は、列をアルファベット、行を数字で表し、列・行の順に指定する。例えば、図1で最も左上の升はa1、最も右下の升はh8である。

プレーヤは、空いている升に石を置いて、縦、横、斜めのどれかの方向に隣接する相手の石を自分の色の石で挟まなければならない。このとき、挟んだ相手の石をすべて反転させ自分の色の石とする。図2に、黒をもつプレーヤが盤に石を置く例を示す。(ア)は図1の中央部分を抜き出したものである。(イ)は、黒を升f5に置いて、升e5の白を挟んだ状態である。(ウ)は、挟んだ白を反転させて黒にした状態である。相手の石を挟むことができないときは、そのプレーヤは石を置くことができないので、手番をパスする。片方の色の石が盤上からなくなったとき、すべての升が石で埋まったとき、又は空いているどの升にも石が置けなくなったとき、ゲームは終了し、自分の色の石の個数の多い方のプレーヤが勝ちとなる。
列挙Pieceは石の面を表す。フィールドBLACKは黒、WHITEは白を表す。
クラスPlayerは、プレーヤを表す。フィールドpieceは、そのプレーヤの石の色を表し、フィールドcountは、盤上での石の個数を表す。
クラスBoardは盤を表し、対戦を支援する次の(1)〜(7)の機能を実現する。
(1) 配列gridは、盤の升を表す。配列要素grid[row][col]において、rowは行、colは列である。例えば、升d2は、grid[1][3]で表される。
(2) 列挙Directionは、配列gridにおいて、縦、横、斜め8方向の隣接する升へのrow及びcolのオフセットを表す定数である。
(3) コンストラクタは、インスタンスを生成し、盤を初期状態にする。
(4) メソッドcanPlaceは、引数playerで与えられたプレーヤが石を置ける升があるかどうかを調べ、あるときはtrue、ないときはfalseを返す。
(5) メソッドreverseは、引数row、colで与えられた升に引数pieceで与えられた色の石を置いた場合、その升から引数dirで与えられた方向に隣接する升の石が反転可能であれば反転する。このメソッドは、反転可能かどうかを判断するために隣接する升に対して自身を再帰的に呼び出す。すなわち、再帰呼出しによって連続する相手の石の先にpieceと同色の石があるかどうかを判定し、あった場合は相手の石を反転して復帰する。すべての再帰呼出しから復帰したとき、挟んだ相手の石はすべて反転し、その反転した石の個数を返す。反転できなかったときは、0又は負の値を返す。
(6) メソッドplaceは、引数positionで指定された升に引数playerで与えられたプレーヤの石が置けるかどうかを判定する。石が置ける場合は、升に石を置き、挟んだ相手の石を反転し、反転した石の個数を返す。この処理は、メソッドreverseを呼び出して行う。指定された升に既に石がある場合、又は相手の石を1個も反転できない場合は、0又は負の値を返し、指定された升に石は置かない。
(7) メソッドdisplayは、盤の状態を表示する。
クラスReversiはゲームを実行する。変数playerは、石を置く番のプレーヤである。変数opponentは、playerの対戦相手である。黒をもつプレーヤが常に先手である。playerが石を置いた後に、それぞれのプレーヤの色の石の個数を更新し、ゲームの終了を判断する。ゲームが終了した場合は、盤の最後の状態を表示してプログラムを終了する。終了しなかったときは、playerとopponentが交代し、ゲームを続行する。メソッドpromptは、引数playerの石の色を表示し、石を置く升を“列行”の形式で入力するように促し、入力された升の位置を文字列で返す。




設問1:
問題文を見る プログラム中のに入れる正しい答えを、解答群の中から選べ。
aに関する解答群
ア:-1, 0
イ:-1, 1
ウ:0, 0
エ:0, 1
オ:1, 0
bに関する解答群
ア:grid[count][0]
イ:grid[nrow][col]
ウ:grid[nrow][ncol]
エ:grid[row][col]
オ:grid[row][ncol]
cに関する解答群
ア:*=
イ:+=
ウ:-=
エ:/=
オ:=
dに関する解答群
ア:++n
イ:n
ウ:n + 1
エ:n - 1
オ:n++
eに関する解答群
ア:board
イ:opponent
ウ:Piece.BLACK
エ:Piece.WHITE
オ:player
模範解答
a:オ、b:ウ、c:イ、d:ウ、e:イ
解説
解答の導き方
この問題では、盤の方向を表す値、石を反転する処理、反転した石の個数の扱い、プレーヤの交代を順に確認します。
まず、配列gridでは行の番号がrow、列の番号がcolです。図1の位置の例では、升d2をgrid[1][3]で表します。つまり、行方向の移動量が1、列方向の移動量が0なら、真下への移動です。
a:南方向の移動量
Directionの各定数は、隣の升へ移動する行と列の増減を表します。例えば、N(-1, 0)は行を1減らして北へ進み、NE(-1, 1)は行を1減らし列を1増やして北東へ進みます。
南へ進むときは、行番号を1増やし、列番号は変えません。したがって、Sに入る値は(1, 0)です。
b:反転する升
reverseは、引数row、colの升からdirの方向へ一つ隣の升を調べます。その升の位置は、nrowとncolに入っています。
例えば、黒を升f5に置いたとき、W方向には白の升e5、その先に黒の升d5があります。reverseは、まずe5からさらに先を調べ、d5が同じ黒だと分かると、戻る途中でe5を黒に変えます。反転するのは、現在調べている隣の升です。その位置はgrid[nrow][ncol]なので、bにはgrid[nrow][ncol]が入ります。
同じ黒の石に隣接している場合は、reverseは0を返します。その結果、呼出し元ではその手前の相手の石が反転されます。一方、途中で空の升に行き当たると負の値が返り、反転処理は行われません。
c:方向ごとの反転数を合計する
placeでは、最初にcountを0にします。そして各方向についてreverseを呼び出し、反転できた個数nが正の場合にcountへ加えます。
複数の方向で反転できる場合、それぞれの個数を合計する必要があります。そのため、cには加算代入の+=が入ります。
d:プレーヤの石の個数を増やす量
placeが返すnは、反転させた相手の石の個数です。プレーヤの石の個数が増えるのは、反転した石だけではありません。新しく置いた石も1個加わります。
したがって、プレーヤの石の個数には「反転したn個」と「新しく置いた1個」を合わせたn + 1を加えます。相手の石の個数からは、反転した石の分だけnを引いています。
e:プレーヤを交代する処理
交代前のplayerをopponentにし、交代前のopponentをplayerにするには、いったん交代前のopponentを別の変数に保存します。
pに入れるのは、交代前のopponentです。次にopponent = playerで交代前のplayerを相手側にし、最後にplayer = pで保存しておいた相手を次の手番にします。よって、eにはopponentが入ります。
誤りやすいポイント
- Sは南方向なので、行番号が増えます。列番号は変わらないため、移動量は(1, 0)です。
- reverseが反転するのは、呼出し元の升ではなく、方向に一つ進んだ升です。その位置を表すのがnrowとncolです。
- reverseの戻り値は、置いた石を含まず、反転した石の個数です。新しく置く石の1個を加えるのは、プレーヤの個数を更新する部分です。
- placeは、升が既に埋まっている場合には負の値を返します。一方、空いている升でもどの方向にも反転できなければ、countは初期値の0のまま返ります。
FAQ
Q: reverseが負の値を返すのは、どのような場合ですか。
A: 方向に沿って調べたとき、盤の端や空の升に行き当たり、相手の石を自分の石で挟めない場合です。この結果は再帰呼出しを通じて戻り、途中の相手の石は反転されません。
A: 方向に沿って調べたとき、盤の端や空の升に行き当たり、相手の石を自分の石で挟めない場合です。この結果は再帰呼出しを通じて戻り、途中の相手の石は反転されません。
Q: 相手の石を反転できなかった場合、placeは負の値を返しますか。
A: いいえ。placeはcountを0で始め、反転数nが正のときだけcountに加えます。どの方向でも反転できなければ、countは0のままです。負の値になるのは、指定した升が既に埋まっている場合などです。
A: いいえ。placeはcountを0で始め、反転数nが正のときだけcountに加えます。どの方向でも反転できなければ、countは0のままです。負の値になるのは、指定した升が既に埋まっている場合などです。
Q: プレーヤの個数にn + 1を加えるのはなぜですか。
A: nは反転した相手の石の個数です。さらに、指定した升にも自分の石を1個置くため、増える個数はn + 1です。
A: nは反転した相手の石の個数です。さらに、指定した升にも自分の石を1個置くため、増える個数はn + 1です。
関連キーワード: 再帰処理、二次元配列、列挙型、戻り値、ループ処理
設問2:
問題文を見る 次の表は、プログラム3のメソッドreverseの仕様をまとめたものである。表中のに入れる正しい答えを、解答群の中から選べ。ここで、bには正しい答えが入っているものとする。

解答群
ア:隣接する升が存在しない、又は隣接する升に石がない場合
イ:隣接する升にPiece以外のクラスのインスタンスが存在する場合
ウ:隣接する升の石が引数pieceで与えられた石と同色の場合
エ:隣接する升の石が引数pieceで与えられた石と異なる色の場合
模範解答
f:ウ、g:ア
解説
解答の導き方
まず、メソッドreverseは、指定された方向に隣接する升を順に調べます。再帰呼出し(メソッドが自分自身を呼び出し、先の状態を調べる処理)で、同じ方向の升を一つずつたどっていきます。
調べた升の内容によって、処理は次のように分かれます。
-
升が盤の外にある場合、または升に石がない場合
盤の外なら隣接する升の情報は設定されず、升が空いている場合もgridの値はnullです。そのためnextがnullのままとなり、プログラムは -1を返します。
したがって、gは「隣接する升が存在しない、又は隣接する升に石がない場合」です。 -
升の石が引数pieceと同じ色の場合
プログラムはnext == pieceのときに0を返します。この升の石は自分の色なので、ここでは反転する石はありません。
したがって、fは「隣接する升の石が引数pieceで与えられた石と同色の場合」です。 -
升の石が引数pieceと異なる色の場合
その升からさらに先を調べます。先に同じ色の石が見つかると、再帰呼出しから戻る途中で相手の石を自分の色に反転し、反転した個数を増やします。先に同じ色の石が見つからなければ、-1が戻り、石は反転しません。
このように、0は「同じ色の石に行き当たり、この升から先では反転しない」ことを表し、-1は「空き升または盤の端に行き当たり、挟めない」ことを表します。
誤りやすいポイント
- 0を「反転できない場合全般」と考えないようにします。このプログラムでは、隣接する升に石がない場合や盤の外に出た場合は -1です。
- 引数pieceと異なる色の石を見つけただけでは、反転できるとは限りません。その先にpieceと同じ色の石が必要です。
- 0が返った後、再帰呼出しから戻る途中で相手の石が反転する場合があります。たとえば、相手の石の先に同じ色の石があれば、途中の相手の石が反転します。
- 選択肢イのような「Piece以外のクラスのインスタンス」は判定対象ではありません。gridに入るのは石を表す値か、石がないことを表すnullです。
FAQ
Q: 同じ色の石を見つけたとき、なぜ1ではなく0を返すのですか?
A: 同じ色の石そのものは反転しないからです。その手前に相手の石があれば、再帰呼出しから戻る途中でそれらを反転し、個数を加算します。
A: 同じ色の石そのものは反転しないからです。その手前に相手の石があれば、再帰呼出しから戻る途中でそれらを反転し、個数を加算します。
Q: 隣接する升が盤の外にある場合と、升が空いている場合は、なぜどちらも -1ですか?
A: どちらの場合も、調べるべき石がありません。プログラムではnextがnullになるため、同じ条件で -1を返します。
A: どちらの場合も、調べるべき石がありません。プログラムではnextがnullになるため、同じ条件で -1を返します。
関連キーワード: 再帰呼出し、配列、列挙型、戻り値、条件分岐



