FC2ブログ

記事一覧

平成19年春期基本情報技術者試験午後問12、JAVA言語

本ページは平成19年春期基本情報技術者試験午後問12、JAVA言語の解説です。
問題文、解答、解説の順で掲載しております。

***********************************************************************************************************
無料から学べる講座
http://ai-light.com/itkeiei/

ニコニコチャンネル
http://ch.nicovideo.jp/ai-light

YouTubeチャンネル
https://www.youtube.com/user/ailightcom

Access・Excel倶楽部
http://ai-light.com/accessclub/

株式会社アイライト公式HP
http://www.ai-light.com/
(Access・Excelでシステム開発会社を探されている法人様はこちら)
***********************************************************************************************************

次の Java プログラムの説明及びプログラムを読んで,設問1,2に答えよ。

〔プログラムの説明〕

三目並べを行うプログラムである。 盤は 3 × 3 の升(ます)目をもち,2人のプレーヤは交互に自分の記号( o 又は x )を書く。 たて,横,斜めでの三つの升の並びを列と呼び,先に,どれかの列で三つの升すべてに, 自分の記号を書いた方を勝ちとする。 また,盤のすべての升に記号が書かれていて,どちらの勝ちでもないときは,引き分けとする。 先手は o ,後手は x を使う。 先手(o)が勝ちの例を図1に示す。

 ×
× 
×

図 1 先手(o)が勝ちの例

列挙型 Mark は記号を表し,先手は Mark.CIRCLE,後手は Mark.CROSS を使うこととする。 また,升に何も書かれていない状態を Mark.BLANKで表す。

クラス TicTacToeBoard は盤を表し,(1) ~ (4) のメソッドをもつ。

(1) Progress put(int x, int y, Mark mark)
盤の位置 (x, y) に mark で表される記号を書く。 盤の状態を列挙型 Progress で返す。 戻り値と盤の状態の対応は,次のとおりである。

Progress.CIRCLE_WON ― 先手の勝ち
Progress.CROSS_WON ― 後手の勝ち
Progress.DRAWN ― 引き分け
Progress.IN_PROGRESS ― 進行中

指定位置が盤の範囲外のときは ArrayIndexOutOfBoundsException を投げる。 既に勝敗が決まっているとき(引き分けを含む)は IllegalStateException を投げ, 次のいずれかに該当するときは IllegalArgumentException を投げる。
① 指定位置に既に記号が書かれている。
② 指定された記号が書けるプレーヤの番ではない。


(2) boolean check(int x, int y, int dx, int dy, Mark mark)
盤のある一つの列の三つの升すべてに,指定された記号が書かれているか否かを
検査する。位置 (x, y),(x + dx, y + dy),(x + 2 * dx, y + 2 * dy)
の三つの升に書かれている記号が,mark と等しければ true を返す。

(3) Mark get(int x, int y)
盤の位置(x, y)に書かれている記号を返す。

(4) void undo()
直前に書いた記号を消す。ただし,勝敗が決まった後は消すことができない。
クラス TicTester はテスト用のプログラムである。実行結果の一部を図2に示す。

Turn : CROSS : IN_PROGRESS
o    x
   x   
o x o
Turn : CIRCLE : IN_PROGRESS
o    x
   x o
o x o
undo
o    x
   x   
o x o
Turn : CIRCLE : CIRCLE_WON
o    x
o    x
o x o
Game is over.

  図2 実行結果の一部
〔参考:列挙(enum)について〕

プログラム中の Mark と Progress はそれぞれ列挙型であり,列挙型 Mark は
三つの列挙定数 CIRCLE,CROSS,BLANK を,列挙型 Progress は四つの 列挙定数 CIRCLE_WON,CROSS_WON,DRAWN,IN_PROGRESS をもつ。 列挙型はクラスの一種であり,列挙定数は列挙型のインスタンスである。

〔プログラム1〕

public enum Mark {
// 記号の種類を表す列挙。各列挙定数では定数 symbol を定義する。
// 定数 symbol の値は各コンストラクタで与えられた値('o'など)である。
CIRCLE('o'), CROSS('x'), BLANK(' ');
public final char symbol;
Mark(char symbol) {
this.symbol = symbol;
}
}

〔プログラム2〕

public class TicTacToeBoard {
// ゲームの進行状況を表す列挙
public enum Progress {CIRCLE_WON, CROSS_WON, DRAWN, IN_PROGRESS}
private Mark[][] board = new Mark[3][3]; // 盤
private Mark turn = Mark.CIRCLE;
private Progress progress = Progress.IN_PROGRESS;
private int count = 0; // 盤に書かれた記号の個数
private int lastx, lasty; // 最後に記号が書かれた位置を保持する。
public TicTacToeBoard() { // 盤を初期化する。
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
board[i][j] ={ a } ;
}
public synchronized Progress put(int x, int y, Mark mark) {
if ({ b })
throw new IllegalStateException();
if (board[x][y] != Mark.BLANK || mark != turn)
throw new IllegalArgumentException();
board[x][y] = mark;
lastx = x;
lasty = y;
count++;
// 勝ちか否かを検査する。
boolean game =
// たてと横の列を検査する。
check(x, 0, 0, 1, mark) || check(0, y, 1, 0, mark) ||
// 斜めの列を検査する。
check(0, 0, 1, 1, mark) || check(0, 2,{ c } , mark);
if (game)
if (turn == Mark.CIRCLE)
progress = Progress.CIRCLE_WON;
else progress = Progress.CROSS_WON;
else if (count == 9) progress = Progress.DRAWN;
// 次に書かれる記号の種類を決定する。
if (turn == Mark.CIRCLE) turn = Mark.CROSS;
else turn = Mark.CIRCLE;
return progress;
}
private boolean check(int x, int y, int dx, int dy, Mark mark) {
return (board[x][y] == mark &&
board[x + dx][y + dy] == mark &&
board[x + 2 * dx][y + 2 * dy] == mark);
}
public Mark get(int x, int y) {
return board[x][y];
}
public synchronized void undo() {
if (progress == Progress.IN_PROGRESS &&
board[lastx][lasty] != Mark.BLANK) {
turn = board[lastx][lasty];
board[lastx][lasty] = Mark.BLANK;
count--;
} else {
throw new IllegalStateException();
}
}
}

〔プログラム3 〕

public class TicTester {
public static void main(String[] args) {
// 記号を書く位置(x, y)を定義した配列。null のときは undo を呼ぶ。
int[][] p = {{0, 0}, {1, 1}, {2, 2}, {2, 0}, {0, 2}, {1, 2},
{2, 1}, null, {0, 1}, {1, 0}};
TicTacToeBoard board = new TicTacToeBoard();
Mark marks[] = {Mark.CIRCLE, Mark.CROSS};
for (int i = 0; i < p.length; i++) {
try {
if (p[i] == null) {
System.out.println("undo");
board.undo();
} else {
Mark turn = marks[{ d }];
System.out.println("Turn : " + turn + " : " +
board.put(p[i][0], p[i][1], turn));
}
for (int y = 0; y < 3; y++) {
for (int x = 0; x < 3; x++)
System.out.print(board.get(x, y).symbol + " ");
System.out.println();
}
} catch (IllegalStateException ise) {
System.out.println("Game is over.");
}
}
}
}


設問1 プログラム中の{  }に入れる正しい答えを, 解答群の中から選べ。

a に関する解答群

ア Mark.BLANK     イ Mark.CIRCLE
ウ Mark.CROSS     エ null


b に関する解答群

ア count < 0
イ count >= 9
ウ progress != Progress.IN_PROGRESS
エ progress == Progress.IN_PROGRESS


c に関する解答群

ア -1, -1      イ -1, 1      ウ 1, -1      エ 1, 1


d に関する解答群

ア i      イ i % 2      ウ i & 2      エ i / 2

設問2 クラス TicTacToeBoard のメソッド undo に関する記述として正しい答えを, 解答群の中から二つ選べ。

解答群

ア 2手目以降で続けて2回呼ぶと,二つ消せる。
イ 2手目以降で続けて2回呼ぶと,呼ばなかった状態に戻る。
ウ 2手目以降で続けて2回呼んでも,消せるのは一つだけである。
エ ゲーム中で先手後手とも,それぞれ1回しか消せない。
オ 消すことができなければ例外を投げる。

***********************************************************************************************************
無料から学べる講座
http://ai-light.com/itkeiei/

ニコニコチャンネル
http://ch.nicovideo.jp/ai-light

YouTubeチャンネル
https://www.youtube.com/user/ailightcom

Access・Excel倶楽部
http://ai-light.com/accessclub/

株式会社アイライト公式HP
http://www.ai-light.com/
(Access・Excelでシステム開発会社を探されている法人様はこちら)
***********************************************************************************************************

********************************************************************************
解答
設問1
答 aア 答 bウ 答 cウ 答 dイ

設問2
答 ウ、オ

********************************************************************************

解説
〔プログラム1〕はpublic enum Markとあり、enumは列挙型と呼ばれるものである。
CIRCLE('o')と記載することで、プログラム内でMark.CIRCLEと記載した際にその値は'o'となる。

問題文より、クラス TicTacToeBoard は盤を表しており、また、クラス TicTacToeBoardのコンストラクタはプログラム中のコメントより盤を初期化している。盤の初期化とは升に何も書かれていない状態にすることである。問題文より、その場合をMark.BLANKで表すとある。よって、解答aはアとなる。

Progress putメソッドにはsynchronizedと記載されている。synchronizedとは処理が同時並行で稼働しないようにする排他制御である。Progress putの処理がまだ終了しない状態で次のProgress putが行われるとシステムに不整合が生じるので、そのようなことが起こらないようにしている。

{ b }
問題文の説明よりIllegalStateExceptionが投げられるのは、既に勝敗が決まっているとき(引き分けを含む)である。これは以下の場合を示す。
Progress.CIRCLE_WON ― 先手の勝ち
Progress.CROSS_WON ― 後手の勝ち
Progress.DRAWN ― 引き分け
よって、bの解答はウとなる。

同様にIllegalArgumentExceptionは次のいずれかに該当するときに投げられる。
「①指定位置に既に記号が書かれている。」場合は、プログラムより
board[x][y] != Mark.BLANKの判定する部分が該当している。

また、「②指定された記号が書けるプレーヤの番ではない。」の部分は
プログラム中のmark != turnの部分が該当している。
実際、プログラム2は初期設定として、
private Mark turn = Mark.CIRCLE;
とあり、turnに初期値を設定しているが、問題文より「先手は Mark.CIRCLE,後手は Mark.CROSS を使うこととする。」とあり、最初の値がMark.CIRCLEでないとエラーとなることがわかる。

メソッドの最終処理部分で
// 次に書かれる記号の種類を決定する。
if (turn == Mark.CIRCLE) turn = Mark.CROSS;
else turn = Mark.CIRCLE;
とあり、turmがMark.CIRCLEであれば、turnをMark.CROSSにして、
turmがMark.CROSSであれば、turnをMark.CIRCLEにしている。
これにより、先手→後手→先手→後手・・・の順が正しく判断されることになる。

// たてと横の列を検査する。
check(x, 0, 0, 1, mark)
の部分で、checkメソッド内を確認すると
(x,0), (x,1), (x,2)のmarkが等しいかをチェックしている。つまり、y軸方向でmarkがそろっているかを確認していることになる。

同様にcheck(0, y, 1, 0, mark)の部分は、
(0,y), (1,y), (2,y)のmarkが等しいかをチェックしている。つまり、x軸方向でmarkがそろっているかを確認していることになる。

// 斜めの列を検査する。
check(0, 0, 1, 1, mark)
部分に関して、斜めでmarkが揃うのは(0,0), (1,1), (2,2)かまたは (0,2), (1,1), (2,0)の場合のいずれかになる。

check(0, 0, 1, 1, mark)は(0,0), (1,1), (2,2)の場合をチェックしているから、
check(0, 2,{ c } , mark)は(0,2), (1,1), (2,0)の場合をチェックしていることになる。cの部分はプログラムよりx軸、y軸の増分を表している。よって、xは1増分し、yは-1増分すればよい。以上より、cはウとなる。

列がそろった際には上記の箇所でgame変数にtrueがセットされる。trueになった場合、現在、入力したプレイヤーが勝ちということである。現在のプレイヤーはmarkにセットされているが、markとturnは等しい値となっている。

よって、turn == Mark.CIRCLEのとき、つまり今のプレイヤーがMark.CIRCLEのとき、Mark.CIRCLEの勝ちであるから、
progress = Progress.CIRCLE_WON
となる。

gameがfalseであってもcountが9であるとき、つまりは升目がすべて埋まっているときは引き分けとなり
progress = Progress.DRAWN
となる。

2次元配列は以下のように定義される。
型名 配列変数名[][] = {{値1_1, 値1_2, ..}, {値2_1, 値2_2, ..}};

よって、以下のように定義されることになる。
p [0][0]=0, p [0][1]=0
p [1][0]=1, p [1][1]=1
p [2][0]=2, p [2][1]=2
p [3][0]=2, p [3][1]=0
p [4][0]=0, p [4][1]=2
p [5][0]=1, p [5][1]=2
p [6][0]=2, p [6][1]=1
p [7][0]=null
p [8][0]=0, p [8][1]=1
p [9][0]=1, p [9][1]=0

この時のp.lengthはp [0]~p [9]の要素数となり、10となる。
p [0][0]~p [0][1]の要素数を取得する場合は、p[0].lengthと記載し、その時の要素数は2となる。

{ d }
%は余りを表すので、
i=0の時、i%2=0
i=1の時、i%2=1
i=2の時、i%2=0
i=3の時、i%2=1
となる。

Mark marks[] = {Mark.CIRCLE, Mark.CROSS}より
marks[0]= Mark.CIRCLE, marks[1]= Mark.CROSSであるから、
Mark.CIRCLE→Mark.CROSS→Mark.CIRCLE→Mark.CROSS→・・・
となり、正しくプレイヤーが交互に入れ替わっているよって解答はイとなる。

&はビット毎のand演算を行う。ビット毎の演算であるから本問では10進数を2進数にしてand演算を行いさらにその結果を10進数に戻すと解が得られることになる。andは両方ともに1の時のみ1となる。また10進数の2は、2進数では10であるから
この場合は、
i=0の時、0 & 10 = 0
i=1の時、1 & 10 = 0
i=2の時、10 & 10=10、2進数10を10進数に戻すと 2
i=3の時、11 & 10=10、2進数10を10進数に戻すと 2(それぞれの桁ごとにビット演算を行う。)
となり、0,0,2,2,・・・となる。
marks[0]、marks[1]しか定義されていないから、marks[2]を指定した場合はエラーとなる。

/は割り算の商であり、i/2は分母も分子も整数であることから商も整数で出力される。
よって
i=0の時、0/2=0
i=1の時、0/2=0
i=2の時、2/2=1
i=3の時、3/2=1
となる。

設問2
選択肢を1つずつ検証していく。

ア 2手目以降で続けて2回呼ぶと,二つ消せる。
undo処理内で
board[lastx][lasty] = Mark.BLANK
で削除が行われている。ここのlastx、lastyはProgress put処理内でセットされているがあくまでも直前の手のみを保存している。よって2回呼び出した場合、1回目の処理は正常に消されるが2回目の処理時、
undo処理のif文の後半の条件部分で
board[lastx][lasty] != Mark.BLANK
はlastx、lastyは1回目の削除処理で呼び出された部分を再度みにいくことになるがすでにその箇所はMark.BLANKがセットされているので、この判別はfalseと判断される。
よって、例外IllegalStateExceptionが投げられることになる。

以上より、選択肢イも正しくなく、ウは正しいことがわかる。

エ ゲーム中で先手後手とも,それぞれ1回しか消せない。
そのような判別条件はないので正しくない。

オ 消すことができなければ例外を投げる。
上記の説明通り、本説明はただしい。


参考文献
enum
http://bleis-tift.hatenablog.com/entry/20090916/1253084400
synchronized
http://www.tohoho-web.com/java/thread.htm


参考:平成19年春期基本情報技術者試験午後問12

***********************************************************************************************************
無料から学べる講座
http://ai-light.com/itkeiei/

ニコニコチャンネル
http://ch.nicovideo.jp/ai-light

YouTubeチャンネル
https://www.youtube.com/user/ailightcom

Access・Excel倶楽部
http://ai-light.com/accessclub/

株式会社アイライト公式HP
http://www.ai-light.com/
(Access・Excelでシステム開発会社を探されている法人様はこちら)
***********************************************************************************************************
関連記事

コメント

コメントの投稿

非公開コメント

adwords

外部リンク

カテゴリーメニュー 改

当社のシステム開発の詳細

当社はaccess-excelでの小規模システム開発を中心に年間に50社以上の開発実績がございます。詳細は06-6599-8890ないしinfo@ai-light.comまで今すぐお問合せください

メールフォーム

こちらからも簡単にメールを送付できます。当社のサービスに関してご質問がございましたら、よろしければ、ご利用ください。直接お電話の場合は06-6599-8890までご連絡ください。

名前:
メール:
件名:
本文:

検索フォーム

広告

最新記事

Lc.ツリーカテゴリー

全記事表示リンク

プロフィール

itkeieinews

Author:itkeieinews
ITと経営ニュースへようこそ!
アイライトIT経営研究会
株式会社アイライト
電話でのお問合せは06-6599-8890
Email:info@ai-light.com

お気に入り

本サイトをそのまま消してしまうと、もう探せなくなってしまうかもしれません。 当社はいずれお役に立てることがあるかと思いますので、よろしければお気に入りにご登録ください。

ブロとも申請フォーム