Javaを使ったオセロゲームの作成

今回はJavaでオセロゲームを作成したいと思います。

とりあえず何かゲームを作りたくてお手軽にできそうだったので オセロをコンソール画面を使って作っていきたいと思います。


環境

Eclipse(Version: 2022-09 (4.25.0))

Java17

1. 仕様

・プレイヤー(自分)は黒石を使う

・CPUは白石を使う

・黒石が先攻で白石が後行でゲームを開始する

・石は相手の石をひっくり返せる所にしか置けない

・ひっくりかえせる石がない場合は相手のターンになる

・石を置く場所がなくなるか、片方の石しか盤面にない状態で試合終了

・試合終了時に石の数が多いほうを勝者にする

・座標の入力は二桁で半角の1~8の数字

・置けない座標を選択した場合は再度入力する

2. 実装

・実装方法としてコンソールから数値入力を受け取りその数値を判定するクラス

・受け取った数値から盤面に石を置いてひっくり返したり盤面を更新するクラス

・更新された盤面を表示するクラス

・CPUが石を置く場所を決めるクラス


と言った実装を行っていく。

3. 定数クラス


オセロの石の表示やメッセージを出力する際に利用する定数クラスを実装する。

othelloConstants.java


package main.java.common;

public class othelloConstants {

	//オセロ石
	public static final String BLACK = "●";
	public static final String WHITE = "○";
	public static final String EMPTY = " ";
	//盤面大きさ
	public static final int STAGE_WIDTH = 10;
	public static final int STAGE_HEIGHT = 10;
	//説明
	public static final String MESSAGE_INPUT = "縦軸横軸で石を置きたい番号をX,Y軸で入力してください(1~8)";
	public static final String MESSAGE_EROOR = "指定された数字をで入力してください";
	public static final String MESSAGE_ORDER_BLACK = "黒のターンです";
	public static final String MESSAGE_ORDER_WHITE = "白(CPU)のターンです";
	public static final String MESSAGE_ORDER_PASS = "石を置ける場所が無いので相手のターンです";
	public static final String MESSAGE_BLACK_WIN = "黒の勝ちです";
	public static final String MESSAGE_WHITE_WIN = "白の勝ちです";
	public static final String MESSAGE_DRAW = "引き分けです";

}

4. メインクラス


メインメソッドを作成する。

ボードの作成や石を置ける位置のリストを作成する。

ボードは各メソッド内で書き換えており戻り値で戻したりはしない。


オセロの動作として

①オセロの置く位置を決める

②石が置けるか判定(置けなければ再び①から)

③石を置いて石をひっくり返す(盤面の更新)

④盤面を表示する


基本的に上記の①~④の作業の繰り返しなのでWhile文を使ってループ処理をしている。

試合終了の条件を満たしたときのみWhileのループを抜けて試合終了としている。


盤面の表示、入力値取得、盤面の更新、試合終了の判定など メインメソッドから各メソッドを呼び出し判定している。


Main.java


package main.java;

import java.util.ArrayList;
import java.util.List;

import main.java.common.othelloConstants;
import main.java.cpu.Decision;
import main.java.logic.Input;
import main.java.logic.Update;
import main.java.logic.View;

public class Main {

	//オセロ盤
	public static String[][] board = new String[othelloConstants.STAGE_HEIGHT][othelloConstants.STAGE_WIDTH];
	//置ける場所
	public static List putBoardList = new ArrayList<>();

	public static void main(String[] args) {

		//入力値
		String inputValue;

		//インスタンス作成
		Input input = new Input();
		Update update = new Update();
		View view = new View();
		Decision decision = new Decision();

		//オセロ盤の初期化
		update.init(board);

		//オセロ盤の表示
		view.showBoard(board);

		while (true) {

			//入力値を受け取る
			inputValue = input.numberEntry(board, putBoardList, update);

			//オセロを置く
			update.write(board, putBoardList, inputValue);

			//オセロ盤の表示
			view.showBoard(board);

			//石を置ける場所があるか確認
			if (!update.boardBuryCheck(board)) {
				break;
			}
			if (!update.boardPutCheck(board, putBoardList)) {
				System.out.println(othelloConstants.MESSAGE_ORDER_PASS);
				update.turnFlg++;
			}

			//盤面上が片方の石だけになった場合は終了
			if (!update.boardStoneCheck(board)) {
				break;
			}
		}

		//試合終了後の判定とメッセージ
		decision.winOrLoss(board);
		System.out.println("終わり");

	}

}

5. 盤面表示クラス


盤面をコンソールに表示するためのクラス 盤面の配列文ループ処理でコンソールへ盤面を出力する。


View.java


package main.java.logic;

import main.java.common.othelloConstants;

public class View {

	public void showBoard(String[][] board) {

		//オセロ盤を描写する
		for (int i = 0; i < othelloConstants.STAGE_HEIGHT; i++) {
			for (int j = 0; j < othelloConstants.STAGE_WIDTH; j++) {
				if (j == 9) {
					System.out.println(board[i][j]);
					break;
				}
				System.out.print(board[i][j]);
			}
		}

	}

}

6. 入力値取得クラス

コンソール画面からプレイヤーの石を置く座標を受け取るクラス。

まだ未実装だがフラグを利用してプレイヤーとCPUのターンを判定している(ターンが変わるごとに数値を変えて判定) プレイヤーのターン時のみ座標を受け付けて入力チェックをしている。

ここでもWhile文を使っており2桁で1~8を入力した場合でないとループを抜け出せない。

正しい入力値を受け取ったらメインクラスに入力値として値を返す。


Input.java


package main.java.logic;

import java.util.List;
import java.util.Scanner;

import main.java.common.othelloConstants;
import main.java.cpu.Decision;

public class Input {

	//石を置く座標を取得する
	public String numberEntry(String[][] board, List putBoardList, Update update) {

		//コンソールから入力された値
		String inputValue;
		//入力チェックのフラグ
		boolean flg;

		//入力した数値を受け取る
		Scanner scanner = new Scanner(System.in);

		//メッセージCPUの場合は自動で入力値を返す
		if (Update.turnFlg % 2 == 0) {
			System.out.println(othelloConstants.MESSAGE_ORDER_BLACK);
		} else {
			System.out.println(othelloConstants.MESSAGE_ORDER_WHITE);
			update.boardPutCheck(board, putBoardList);
			inputValue = Decision.cpu(putBoardList);
			System.out.println(inputValue);
			return inputValue;
		}
		System.out.println(othelloConstants.MESSAGE_INPUT);

		//入力チェックがOKになるまで何度も行う。
		while (true) {
			inputValue = scanner.nextLine();
			flg = inputCheck(inputValue);
			if (flg)
				break;
		}

		return inputValue;
	}

	//入力された値の入力チェック
	public boolean inputCheck(String inputValue) {

		//1~8の数字で2桁のみOKそれ以外はNG
		if (inputValue.matches("[1-8]{2}") && inputValue.length() == 2) {
			return true;
		}
		System.out.println(othelloConstants.MESSAGE_EROOR);
		return false;
	}
}

7. 盤面更新クラス


ここのクラスでは主に盤面の更新処理を行っている。

最初に盤面の初期表示として壁面に座標の数値を記載し 中央に黒と白の石を配置しその他を空きスペースとして盤面を更新する。

その後入力値等を受け取るのでその入力値に石を置いた際に 上下左右ひっくり返せる石があるかどうか確認を行いひっくり返せる場合は 置いた石の色に色を更新する。

また置けない場合は再度入力値の入力を求める。


石がひっくり返せるかの判定は石を置いた座標の八方向に対して、 自分の置いた石と違う色の石があればそのまま先の座標を見ていき 自分の石がその先に置いてあった場合はそれまでの石をひっくり返すようにしている。

確認の途中に空の列や配列の端まで言った場合はひっくり返せないものとして処理を終了する。


また他の処理としては上記と同じような処理で盤面の石を置いていない座標で 現在のターンの石で盤面に置いた場合ひっくり返せるか判定する処理や(無ければ相手のターンへ)、 盤面における場所があるかの判定や、盤面に片方の石しかないかなど 試合終了やパスの際の条件等もここで調査している。


Update.java


package main.java.logic;

import java.util.List;

import main.java.common.othelloConstants;

public class Update {

	//白黒のターン判定するために使用。偶数が黒、奇数が白
	public static int turnFlg = 0;

	public void init(String[][] board) {

		//オセロ盤の初期化
		for (int i = 0; i < othelloConstants.STAGE_HEIGHT; i++) {
			for (int j = 0; j < othelloConstants.STAGE_WIDTH; j++) {
				if (i == 0 || i == 9) {
					board[i][j] = Integer.valueOf(j).toString();
				} else if (i >= 0 && i < 10 && (j == 0 || j == 9)) {
					board[i][j] = Integer.valueOf(i).toString();
				} else {
					board[i][j] = othelloConstants.EMPTY;
				}
			}
		}

		//石の初期配置
		board[4][4] = othelloConstants.WHITE;
		board[4][5] = othelloConstants.BLACK;
		board[5][4] = othelloConstants.BLACK;
		board[5][5] = othelloConstants.WHITE;

	}

	//石をひっくり返せたらひっくりかえす
	public boolean write(String[][] board, List putBoardList, String inputValue) {

		//X軸Y軸
		int x;
		int y;

		//X軸Y軸の取得
		x = Integer.valueOf(inputValue.substring(0, 1));
		y = Integer.valueOf(inputValue.substring(1, 2));

		//そもそも石が置けるか
		if (board[x][y] != othelloConstants.EMPTY) {
			return false;
		}

		//石をボードに置く
		boardPut(board, x, y);

		return true;
	}

	//石が置けるか判断して置けたらひっくりかえす
	public boolean boardPut(String[][] board, int x, int y) {

		//ひっくりかえせたらtrueに

8. CPUクラス

勝敗の判定を行うのとCPUが石を置く座標を決めるクラス。

勝敗は試合終了時に盤面においてある石の数が多いほうを勝者とする。

CPUの座標はUpdateクラスから取得した石を置ける座標の中から ランダムで座標を選び石をおく。


Decision.java


package main.java.cpu;

import java.util.List;
import java.util.Random;

import main.java.common.othelloConstants;

public class Decision {

	//勝敗の判定をする
	public void winOrLoss(String[][] board) {

		//黒白のカウント
		int black = 0;
		int white = 0;

		//白の石と黒の石どちらが多いか確認する
		for (int i = 1; i < 9; i++) {
			for (int j = 1; j < 9; j++) {
				if (board[i][j] == othelloConstants.BLACK) {
					black++;
				} else if (board[i][j] == othelloConstants.WHITE) {
					white++;
				}
			}
		}

		//試合結果の出力
		if (black > white) {
			System.out.println(othelloConstants.MESSAGE_BLACK_WIN + "黒:" + black + "白:" + white);
		} else if (black < white) {
			System.out.println(othelloConstants.MESSAGE_WHITE_WIN + "黒:" + black + "白:" + white);
		} else {
			System.out.println(othelloConstants.MESSAGE_DRAW + "黒:" + black + "白:" + white);
		}
	}

	//石を置ける場所を判断してランダムで配置する
	public static String cpu(List putBoardList) {
		Random random = new Random();
		int listNumber = random.nextInt(putBoardList.size());
		return putBoardList.get(listNumber);

	}
}

9. まとめ

オセロゲームの実装とCPUの作成ができたので良かった。

書いていてなかなか長短な感じがしていてもう少しクラスの 分け方や簡略化したもので実装できればよいと感じた。

CPUに関してはあまり凝ったつくりになっていないので今後の目標として 考えて行動するような物が実装できればいいなと思った。