基本情報技術者試験の過去問と解説
[TOP] [午前分野別] [午後分野別] [キーワード索引] [令和元年秋午前] [令和元年秋午後]

平成19年 春期 基本情報技術者 午後 問12
問12   Java

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

〔プログラムの説明〕

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

 o      x  
 o    x    
 o    x    o  

図 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 を投げる。

@ 指定位置に既に記号が書かれている。

A 指定された記号が書けるプレーヤの番ではない。

(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] = ;
   }
   public synchronized Progress put(int x, int y, Mark mark) {
      if ()
         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, , 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[];
               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

解答 a ←クリックすると正解が表示されます

解答 b ←クリックすると正解が表示されます

解答 c ←クリックすると正解が表示されます

解答 d ←クリックすると正解が表示されます

基本情報技術者試験


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

解答群

ア 2手目以降で続けて2回呼ぶと,二つ消せる。

イ 2手目以降で続けて2回呼ぶと,呼ばなかった状態に戻る。

ウ 2手目以降で続けて2回呼んでも,消せるのは一つだけである。

エ ゲーム中で先手後手とも,それぞれ1回しか消せない。

オ 消すことができなければ例外を投げる。

解答 ←クリックすると正解が表示されます

[←前の問題] [次の問題→] [問題一覧表] [分野別] [基本情報技術者試験TOP ]