JAVA練習問題4-☆5:マルバツゲーム(Tic-Tac-Toe)を作成しよう

記事内に商品プロモーションを含む場合があります

JAVAの初心者向け問題4-☆5:マルバツゲーム(Tic-Tac-Toe)を作成しよう

この問題を解くために必要な知識:
【レベル1~3の知識】
コメントの書き方、変数と定数、基本データ型とプリミティブ型、四則演算と論理演算、入力と出力(Scannerクラス)、配列、分岐処理(if、if~else、switch)、繰り返し処理(for、while、do~while)、メソッドの定義と呼び出し、メソッドの戻り値、メソッドのオーバーロード、例外処理(try-catch文、throws、throw)、レベル1と2の知識、コレクションの基礎、コレクション(List, ArrayList)、コレクション(HashSet, TreeSet)、コレクション(HashMap, TreeMap)、

【レベル4の知識】
クラスの定義と使用、インスタンス、コンストラクタ、ゲッターとセッター、カプセル化、クラスメンバ、インスタンスメソッド、クラスの継承抽象クラスインターフェース

<<前の問題 問題集Top 次の問題>>

JAVA練習問題4-☆5:マルバツゲーム(Tic-Tac-Toe)を作成しよう





簡単なマルバツゲーム(Tic-Tac-Toe)を作成しましょう。

このゲームでは、2人のプレイヤーが交互にマル(○)またはバツ(×)を入力し、縦、横、または斜めのいずれかで同じ記号が3つ並んだら勝ちとなります。

問題文から回答を導き出すのが難しいと感じる場合は、先に正解のコードと解説を見て内容を理解するようにしましょう。

この問題の要件

以下の要件に従ってコードを書いて下さい。

  • 3×3のゲームボードを作成するために、2次元配列 char[][] board を使用すること。
  • ゲームボードの初期状態は全て空白(’ ‘)に設定すること。
  • 各プレイヤーのターンごとに、現在のゲームボードを表示し、プレイヤーに行を指定させること。
  • プレイヤーが選んだ行と列にマル(○)またはバツ(×)を配置すること。
  • 勝利条件(縦、横、斜めのいずれかに同じ記号が3つ並ぶ)をチェックすること。
  • 勝者が決まった場合、勝者を表示してゲームを終了すること。
  • ゲームボードが全て埋まった場合は引き分けとし、ゲームを終了すること。
  • プレイヤーの入力が無効な場合は、再入力を促すこと。

ただし、以下のような実行結果となること。

----- ↓出力される結果の例↓ -----

-------------
|   |   |   | 
-------------
|   |   |   | 
-------------
|   |   |   | 
-------------
プレイヤー X、行と列を入力してください: 
0 0
-------------
| X |   |   | 
-------------
|   |   |   | 
-------------
|   |   |   | 
-------------
プレイヤー O、行と列を入力してください: 
1 1
-------------
| X |   |   | 
-------------
|   | O |   | 
-------------
|   |   |   | 
-------------
...
プレイヤー X の勝利です!

----- ↑出力される結果の例↑ -----

この問題を解くのヒント

1からコードを組み立てることが難しい場合は、以下のヒントを開いて参考にしましょう。

ヒント1【コードの構成を見る】

正解のコードは上から順に以下のような構成となっています。

1.import文
 1-1.Scannerクラスのインポート
2.TicTacToeクラスの定義
 2-1.メンバ変数の定義
  2-1-1.char型の2次元配列board
  2-1-2.char型のcurrentPlayer
 2-2.コンストラクタの定義
  2-2-1.board配列の初期化(3×3)
  2-2-2.currentPlayerの初期化(’X’)
  2-2-3.initializeBoardメソッドの呼び出し
 2-3.ゲームボードを空白で初期化するinitializeBoardメソッドの定義
  2-3-1.2重ループでboard配列を空白で埋める
 2-4.ゲームボードを表示するprintBoardメソッドの定義
  2-4-1.ボードの状態をコンソールに表示
 2-5.マークを配置するplaceMarkメソッドの定義
  2-5-1.指定された位置が有効かどうかをチェック
  2-5-2.有効な場合はcurrentPlayerのマークを配置
 2-6.勝利条件をチェックするcheckForWinメソッドの定義
  2-6-1.行、列、斜めのいずれかが揃っているかをチェック
 2-7.行をチェックするcheckRowsメソッドの定義
  2-7-1.各行が揃っているかをチェック
 2-8.列をチェックするcheckColumnsメソッドの定義
  2-8-1.各列が揃っているかをチェック
 2-9.斜めをチェックするcheckDiagonalsメソッドの定義
  2-9-1.2つの斜めのラインが揃っているかをチェック
 2-10.プレイヤーを切り替えるchangePlayerメソッドの定義
  2-10-1.currentPlayerを’X’から’O’、またはその逆に切り替える
 2-11.メインメソッドの定義
  2-11-1.TicTacToeオブジェクトの作成
  2-11-2.Scannerオブジェクトの作成
  2-11-3.ゲームループ(whileループ)
   2-11-3-1.ゲームボードの表示(printBoardメソッドの呼び出し)
   2-11-3-2.プレイヤーの入力を受け取る
   2-11-3-3.マークを配置する(placeMarkメソッドの呼び出し)
   2-11-3-4.勝利条件をチェックする(checkForWinメソッドの呼び出し)
   2-11-3-5.勝者が決まった場合、結果を表示
   2-11-3-6.引き分けの場合、結果を表示
   2-11-3-7.プレイヤーの切り替え(changePlayerメソッドの呼び出し)
  2-11-4.Scannerオブジェクトを閉じる
 2-12.ボードが満杯かチェックするisBoardFullメソッドの定義
  2-12-1.ボード内に空白が残っているかをチェック

ヒント2【穴埋め問題にする】

以下のコードをコピーし、コメントに従ってコードを完成させて下さい。

import java.util.Scanner;

public class TicTacToe {
private char[][] board;
private char currentPlayer;

// コンストラクタ:ゲームボードを初期化
public TicTacToe() {
board = new char[ 3 ][ 3 ];
currentPlayer = 'X';
initializeBoard();
}

// ゲームボードを空白で初期化するメソッド
public void initializeBoard() {
// ***ここにゲームボードを空白で初期化するコードを書いてください。***
// ***2重ループを使用してboard配列の各要素に' 'を設定してください。***
}

// ゲームボードを表示するメソッド
public void printBoard() {
System.out.println("-------------");
// ***ここにゲームボードを表示するコードを書いてください。***
// ***2重ループを使用して各要素を表示し、行区切りを表示してください。***
}

// マークを配置するメソッド
public boolean placeMark(int row, int col) {
// ***ここにマークを配置するコードを書いてください。***
// ***指定された位置が有効かどうかをチェックし、有効ならcurrentPlayerのマークを配置してください。***
}

// 勝利条件をチェックするメソッド
public boolean checkForWin() {
return (checkRows() || checkColumns() || checkDiagonals());
}

// 行をチェックするメソッド
private boolean checkRows() {
// ***ここに行をチェックするコードを書いてください。***
// ***各行がcurrentPlayerのマークで揃っているかをチェックしてください。***
}

// 列をチェックするメソッド
private boolean checkColumns() {
// ***ここに列をチェックするコードを書いてください。***
// ***各列がcurrentPlayerのマークで揃っているかをチェックしてください。***
}

// 斜めをチェックするメソッド
private boolean checkDiagonals() {
// ***ここに斜めをチェックするコードを書いてください。***
// ***2つの斜めのラインがcurrentPlayerのマークで揃っているかをチェックしてください。***
}

// プレイヤーを切り替えるメソッド
public void changePlayer() {
currentPlayer = (currentPlayer == 'X') ? 'O' : 'X';
}

// メインメソッド:ゲームの進行を制御
public static void main(String[] args) {
TicTacToe game = new TicTacToe();
Scanner scanner = new Scanner(System.in);
boolean gameWon = false;

while (!gameWon) {
game.printBoard();
int row, col;
do {
System.out.println("プレイヤー " + game.currentPlayer + "、行と列を入力してください: ");
row = scanner.nextInt();
col = scanner.nextInt();
} while (!game.placeMark(row, col));

gameWon = game.checkForWin();
if (gameWon) {
game.printBoard();
System.out.println("プレイヤー " + game.currentPlayer + " の勝利です!");
} else if (game.isBoardFull()) {
game.printBoard();
System.out.println("引き分けです!");
break;
} else {
game.changePlayer();
}
}
scanner.close();
}

// ボードが満杯かチェックするメソッド
public boolean isBoardFull() {
// ***ここにボードが満杯かどうかをチェックするコードを書いてください。***
// ***2重ループを使用してboard配列に空白が残っているかをチェックしてください。***
}
}

 

このヒントを見てもまだ回答を導き出すのが難しいと感じる場合は、先に正解のコードと解説を見て内容を理解するようにしましょう。



解答例

この問題の一つの正解例とそのコードの解説を以下に示します。

正解コードの例

例えば以下のようなプログラムが考えられます。

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

import java.util.Scanner;

public class TicTacToe {
private char[][] board;
private char currentPlayer;

// コンストラクタ:ゲームボードを初期化
public TicTacToe() {
board = new char[ 3 ][ 3 ];
currentPlayer = 'X';
initializeBoard();
}

// ゲームボードを空白で初期化するメソッド
public void initializeBoard() {
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
board[i][j] = ' ';
}
}
}

// ゲームボードを表示するメソッド
public void printBoard() {
System.out.println("-------------");
for (int i = 0; i < 3; i++) {
System.out.print("| ");
for (int j = 0; j < 3; j++) {
System.out.print(board[i][j] + " | ");
}
System.out.println();
System.out.println("-------------");
}
}

// マークを配置するメソッド
public boolean placeMark(int row, int col) {
if (row >= 0 && row < 3 && col >= 0 && col < 3 && board[row][col] == ' ') {
board[row][col] = currentPlayer;
return true;
}
return false;
}

// 勝利条件をチェックするメソッド
public boolean checkForWin() {
return (checkRows() || checkColumns() || checkDiagonals());
}

// 行をチェックするメソッド
private boolean checkRows() {
for (int i = 0; i < 3; i++) {
if (board[i][0] == currentPlayer && board[i][ 1 ] == currentPlayer && board[i][ 2 ] == currentPlayer) {
return true;
}
}
return false;
}

// 列をチェックするメソッド
private boolean checkColumns() {
for (int i = 0; i < 3; i++) {
if (board[0][i] == currentPlayer && board[ 1 ][i] == currentPlayer && board[ 2 ][i] == currentPlayer) {
return true;
}
}
return false;
}

// 斜めをチェックするメソッド
private boolean checkDiagonals() {
return ((board[0][0] == currentPlayer && board[ 1 ][ 1 ] == currentPlayer && board[ 2 ][ 2 ] == currentPlayer) ||
(board[0][ 2 ] == currentPlayer && board[ 1 ][ 1 ] == currentPlayer && board[ 2 ][0] == currentPlayer));
}

// プレイヤーを切り替えるメソッド
public void changePlayer() {
currentPlayer = (currentPlayer == 'X') ? 'O' : 'X';
}

// メインメソッド:ゲームの進行を制御
public static void main(String[] args) {
TicTacToe game = new TicTacToe();
Scanner scanner = new Scanner(System.in);
boolean gameWon = false;

while (!gameWon) {
game.printBoard();
int row, col;
do {
System.out.println("プレイヤー " + game.currentPlayer + "、行と列を入力してください: ");
row = scanner.nextInt();
col = scanner.nextInt();
} while (!game.placeMark(row, col));
gameWon = game.checkForWin();
if (gameWon) {
game.printBoard();
System.out.println("プレイヤー " + game.currentPlayer + " の勝利です!");
} else if (game.isBoardFull()) {
game.printBoard();
System.out.println("引き分けです!");
break;
} else {
game.changePlayer();
}
}
scanner.close();
}

// ボードが満杯かチェックするメソッド
public boolean isBoardFull() {
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (board[i][j] == ' ') {
return false;
}
}
}
return true;
}
}

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

正解コードの解説

このコードは、プレイヤー同士が交互にマークを配置し、先に横、縦、または斜めに3つのマークを揃えた方が勝ちとなる「マルバツゲーム」を実装しています。

以下に、コードの各部分を初心者向けに説明します。

クラスとフィールドの宣言

private char[][] board;
private char currentPlayer;
  • board: 3×3の二次元配列で、ゲームの盤面を表します。各セルは空白(’ ‘)、’X’、’O’のいずれかを持ちます。
  • currentPlayer: 現在のプレイヤーを表します。’X’または’O’の値を持ち、ゲーム中に交互に変わります。

コンストラクタ:ゲームボードの初期化

public TicTacToe() {
    board = new char[ 3][ 3];
    currentPlayer = 'X';
    initializeBoard();
}

このコンストラクタは、ゲームが始まる際にボードを作成し、すべてのセルを空白に初期化します。

また、最初のプレイヤーを’X’に設定します。

ゲームボードを空白で初期化するメソッド

public void initializeBoard() {
    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < 3; j++) {
            board[i][j] = ' ';
        }
    }
}

このメソッドでは、ボード上のすべてのセルを空白に設定し、ゲームを最初の状態にリセットします。

ゲームボードを表示するメソッド

public void printBoard() {
    System.out.println("-------------");
    for (int i = 0; i < 3; i++) {
        System.out.print("| ");
        for (int j = 0; j < 3; j++) {
            System.out.print(board[i][j] + " | ");
        }
        System.out.println();
        System.out.println("-------------");
    }
}

printBoardは、現在のボードの状態を表示するためのメソッドです。横線や縦線で区切られた見やすい形式で出力します。

プレイヤーがマークを配置するメソッド

public boolean placeMark(int row, int col) {
    if (row >= 0 && row < 3 && col >= 0 && col < 3 && board[row][col] == ' ') {
        board[row][col] = currentPlayer;
        return true;
    }
    return false;
}

placeMarkは、プレイヤーが指定したセルにマーク(’X’または’O’)を配置します。セルが空いていれば配置が可能です。

勝利条件を確認するメソッド

public boolean checkForWin() {
    return (checkRows() || checkColumns() || checkDiagonals());
}

このメソッドは、プレイヤーが勝利したかどうかを確認します。行、列、または斜めのいずれかで同じマークが3つ並んでいるかをチェックします。

行・列・斜めを確認するメソッド

private boolean checkRows() { /* 行の確認 */ }
private boolean checkColumns() { /* 列の確認 */ }
private boolean checkDiagonals() { /* 斜めの確認 */ }

それぞれのメソッドは、勝利条件を満たしているかどうかを確認するためのものです。これらのメソッドがtrueを返すと、ゲームが終了します。

プレイヤーを切り替えるメソッド

public void changePlayer() {
    currentPlayer = (currentPlayer == 'X') ? 'O' : 'X';
}

このメソッドは、現在のプレイヤーを’X’から’O’、または’O’から’X’に切り替える役割を持っています。

ボードが満杯かどうか確認するメソッド

public boolean isBoardFull() {
    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < 3; j++) {
            if (board[i][j] == ' ') {
                return false;
            }
        }
    }
    return true;
}

isBoardFullは、ボードがすべて埋まっているかを確認します。埋まっていれば引き分けです。

メインメソッドでゲームを進行

public static void main(String[] args) {
    TicTacToe game = new TicTacToe();
    // ゲームの進行とプレイヤー入力処理
}

mainメソッドでは、ゲームのロジックを制御します。プレイヤーの入力を受け取り、ゲームの進行を管理します。

まとめ

このコードは、基本的な二次元配列、条件分岐、ループなど、プログラミングの基礎を学ぶのに適した内容です。

また、ゲームのロジックを考えることで、設計の力も養われます。

<<前の問題 問題集Top 次の問題>>




この問題への質問・コメント

この問題を作成するにあたりAIを活用しています。

問題ないことは確認していますが、もし間違いや表現の違和感などありましたら、ご指摘頂けると大変助かります。






    JAVA練習問題集へ戻る
    トップページへ戻る