【Java】

第4回 Javaでオセロを作成しよう

オセロをひっくり返す処理を実装しよう

投稿日 2021/01/05 更新日 2021/01/05


こんにちは。当サイトの管理者「元木皇天」です。

さて、Javaでオセロを作ろうの第4回です。第1回からの続きですので、まだの方はぜひ過去の回から作成をしてください。

Javaでオセロを作ろうシリーズ一覧へ

今回はボード上にオセロを置いた後、敵のオセロをひっくり返す処理を実装していきたいと思います。

開発環境
OS:macOS
Java:11.0.7
Eclipse:2020-03

今回の目標

今回の目標は、オセロを置いた後に敵のオセロを自分のオセロの色で挟めたら、敵のオセロをひっくり返して ボードを表示することです。実際の動きは以下のようになります。

オセロを開始します.
| |1|2|3|4|5|6|7|8|
|1| | | | | | | | |
|2| | | | | | | | |
|3| | | | | | | | |
|4| | | |◯|●| | | |
|5| | | |●|◯| | | |
|6| | | | | | | | |
|7| | | | | | | | |
|8| | | | | | | | |
黒の数:2
白の数:2
横の座標を1~8の半角数字で入力してください
6
縦の座標を1~8の半角数字で入力してください
5
| |1|2|3|4|5|6|7|8|
|1| | | | | | | | |
|2| | | | | | | | |
|3| | | | | | | | |
|4| | | |◯|●| | | |
|5| | | |●|●|●| | |
|6| | | | | | | | |
|7| | | | | | | | |
|8| | | | | | | | |
黒の数:4
白の数:1

クラスの作成

今回は新たに「CopyBoard.java」というクラスを作成します。

以下の画像の赤で囲んでいる箇所が今回の追加対象です。

作成フォルダ一覧

処理の流れ

当サイトでは以下フローの処理を行うことで、敵のオセロをひっくり返す処理を実装します。 今までよりも少し複雑なので、どういう処理をどういう順序で行うのかを理解しないと、 途中で何のコードを書いているのか分からなくなってしまいます。 まずは以下のフローをみていただきどのような処理を実装していくのか ザックリと頭に入れていただければと思います。

処理のフロー図

では早速作成していきましょう。

ボード情報をコピーする

今回の処理でまず初めに行う処理が、「オセロのボード情報をコピーする」という処理です。

なぜコピーが必要かと言いますと以下のような処理を行うためです。

①オリジナルのオセロボードのコピーを作成する
②コピーを編集する(オセロをひっくり返す)
③出来上がったコピーを、オリジナルのオセロボードに上書きする

ローカル環境で作成して本番環境にアップするというようなイメージを持っていただければと思います。

また、ある程度プログラミングに慣れてきてもうっかりやってしまうのが、配列のコピーに「=」を使用してしまうことです。 配列は参照型なので、for文を使用して、配列一つずつの値を取り出し、代入するという手順を踏まなくてはいけないので注意が必要です。 そのため、その処理を行うためのクラスとして「CopyBoard.java」を作成しています。


では、2次元配列をコピーするプログラムを作成しましょう。

--CopyBoard.java--

package osero.model;

public class CopyBoard {

    //2次元配列のコピー
    public String[][] copySaveBoard(String board[][]) {

        //returnする2次元配列の作成
        String saveBoard[][] = new String[9][9];

        //引数で渡した2次元配列をfor文で1つずつ取り出し格納する
        for(int i=0; i<9; i++) {
            for(int j=0; j<9; j++) {
                String str = board[i][j];
                saveBoard[i][j] = str;
            }
        }
        return saveBoard;
    }
}

ここで作成した「CopyBoard.java」はこの後作成する「ReverseOsero.java」で使用します。

左側のオセロをひっくり返す

ここからいよいよオセロをひっくり返す処理を作成します。

オセロは上下左右とそれぞれの斜めを合わせた全8方向に、ひっくり返す対象があります。 ですので、ひっくり返す処理も8つ作成する必要があります。 まずは、オセロを置いた「左側」のみひっくり返す処理を実装していきましょう。

--ReverseOsero.java--

package osero.model;

import osero.Main;

public class ReverseOsero {

    CopyBoard CB = new CopyBoard();

    //ひっくり返す座標を保持するボードの作成
    public String copyBoard[][] = CB.copySaveBoard(Main.board);

    //省略

    //オセロボードの情報をcopyBoardに保存する
    public void makeSaveBoard(String[][] board) {
        copyBoard = CB.copySaveBoard(Main.board);
    }

    //左をひっくり返す
    public void reverseLeft(int x, int y) {
        while(true) {

            //端に到達したらループを抜ける
            if(x < 2) {
                //コピーボードをリセットする
                copyBoard = CB.copySaveBoard(Main.board);
                break;
            }

            //隣が相手の色だった場合
            if(Main.board[x-1][y] == Main.enemyOsero) {
                //ひっくり返す座標をコピーボードに保存する
                copyBoard[x-1][y] = Main.userOsero;

            //隣が自分の色だった場合
            }else if(Main.board[x-1][y] == Main.userOsero){
                //変更内容をオリジナルのボードに反映する
                Main.board = CB.copySaveBoard(copyBoard);
                break;

            //隣が空白だった場合
            }else{
                //コピーボードをリセットする
                copyBoard = CB.copySaveBoard(Main.board);
                break;
            }
            x--;
        }
    }
}

次に、Main.javaに上記のメソッドを使用するためのコードを追記します。

--Main.java--

package osero;

import java.util.Scanner;

import osero.config.StartConfig;
import osero.middleware.CheckInputValue;
import osero.model.ReverseOsero;
import osero.view.ShowBoard;

public class Main {

    //省略

    //ユーザのオセロ情報
    public static String userOsero = black;
    public static String enemyOsero = white;

    public static void main(String[] args) {

        //インスタンスの作成
        StartConfig SC = new StartConfig();
        ShowBoard SB = new ShowBoard();
        Scanner sc = new Scanner(System.in);
        CheckInputValue CIV = new CheckInputValue();
        ReverseOsero RO = new ReverseOsero();

        //省略

        while(true) {
            //省略
        }

        while(true) {
            //省略
        }

        //入力した座標にオセロを置く
        RO.putOsero(inputX, inputY);

        //オリジナルボードをコピーボードにコピーする
        RO.makeSaveBoard(Main.board);

        //ひっくり返す処理
        RO.reverseLeft(inputX, inputY);

        //ボードを表示する
        SB.showBoard(board);
    }
}

まず、「ReverseOsero.java」で作成した「reverseLeft」メソッドについて説明します。

このメソッドでは、引数で渡された座標をもとに処理を行なっています。 初めに、オセロを置いた場所がボードの端かどうかをチェックしています。 端だった場合は初めのif文でコピーボードを初期状態に戻し、breakしています。


次のif-else文では、左のマスに以下の3つのどれが置いてあるかによって処理が変わります

①相手の色のオセロ
②自分の色のオセロ
③空白

まず、③の場合ですが、この場合だとひっくり返す処理を行う必要がないので、コピーボードをリセットして終了します。

②の場合もひっくり返す処理を行いませんが、コピーボードをオリジナルに上書きしています。

最後に①の場合は、左のオセロの色を自分のオセロの色に変更(ひっくり返す)して、次のマスに何が置かれているか確認します。

①の場合「x座標」をデクリメントして、while文のループでもう一つ左の座標を確認します。そして同様に先程の①〜③の判定をします。


ここで、②だった場合はひっくり返した状態が、オリジナルのボードに上書かれるため、ひっくり返した処理が完了となります。

ここではオセロを置いてひっくり返す処理を左側のマスのみを対象としているので、残り7方向の処理を実装していきます。

全方向のオセロをひっくり返す処理の作成

最後に全方向のオセロをひっくり返す処理を実装します。

基本的には、先程の左側のオセロをひっくり返す処理と同じロジックでプログラムを組んでいきます。

結構長いコードなのであらかじめご了承ください。

--ReverseOsero.java--

package osero.model;

import osero.Main;

public class ReverseOsero {

    //省略

    //省略(左をひっくり返す処理)

    //右をひっくり返す
    public void reverseRight(int x, int y) {
        //座標がボードの端に到達した場合ループを抜ける
        while(true) {
            if(x > 7) {
                //コピーボードをリセットする
                copyBoard = CB.copySaveBoard(Main.board);
                break;
            }

            //隣が相手の色だった場合
            if(Main.board[x+1][y] == Main.enemyOsero) {
                //ひっくり返す座標をコピーボードに保存する
                copyBoard[x+1][y] = Main.userOsero;

            //隣が自分の色だった場合
            }else if(Main.board[x+1][y] == Main.userOsero){
                //変更内容をオリジナルのボードに反映する
                Main.board = CB.copySaveBoard(copyBoard);
                break;

            //隣が空白だった場合
            }else {
                //コピーボードをリセットする
                copyBoard = CB.copySaveBoard(Main.board);
                break;
            }
            x++;
        }
    }

    //上をひっくり返す
    public void reverseTop(int x, int y) {
        while(true) {
            //座標がボードの上端に到達した場合の処理
            if(y < 2) {
                //コピーボードをリセットする
                copyBoard = CB.copySaveBoard(Main.board);
                break;
            }

            //隣が相手の色だった場合
            if(Main.board[x][y-1] == Main.enemyOsero) {
                //ひっくり返す座標をコピーボードに保存する
                copyBoard[x][y-1] = Main.userOsero;

            //隣が自分の色だった場合
            }else if(Main.board[x][y-1] == Main.userOsero){
                //変更内容をオリジナルのボードに反映する
                Main.board = CB.copySaveBoard(copyBoard);
                break;

            //隣が空白だった場合
            }else{
                //コピーボードをリセットする
                copyBoard = CB.copySaveBoard(Main.board);
                break;
            }
            y--;
        }
    }

    //下をひっくり返す
    public void reverseBottom(int x, int y) {
        while(true) {
            //座標がボードの端に到達した場合の処理
            if(y > 7) {
                //コピーボードをリセットする
                copyBoard = CB.copySaveBoard(Main.board);
                break;
            }

            //隣が相手の色だった場合
            if(Main.board[x][y+1] == Main.enemyOsero) {
                //ひっくり返す座標をコピーボードに保存する
                copyBoard[x][y+1] = Main.userOsero;

            //隣が自分の色だった場合
            }else if(Main.board[x][y+1] == Main.userOsero){
                //変更内容をオリジナルのボードに反映する
                Main.board = CB.copySaveBoard(copyBoard);
                break;

            //隣が空白だった場合
            }else{
                //コピーボードをリセットする
                copyBoard = CB.copySaveBoard(Main.board);
                break;
            }
            y++;
        }
    }

    //左上をひっくり返す
    public void reverseLeftTop(int x, int y) {
        while(true) {
            //座標がボードの上端または左端に到達した場合の処理
            if(x < 2 || y < 2) {
                //コピーボードをリセットする
                copyBoard = CB.copySaveBoard(Main.board);
                break;
            }

            //隣が相手の色だった場合
            if(Main.board[x-1][y-1] == Main.enemyOsero) {
                //ひっくり返す座標をコピーボードに保存する
                copyBoard[x-1][y-1] = Main.userOsero;

            //隣が自分の色だった場合
            }else if(Main.board[x-1][y-1] == Main.userOsero){
                //変更内容をオリジナルのボードに反映する
                Main.board = CB.copySaveBoard(copyBoard);
                break;

            //隣が空白だった場合
            }else{
                //コピーボードをリセットする
                copyBoard = CB.copySaveBoard(Main.board);
                break;
            }
            x--;
            y--;
        }
    }

    //右上をひっくり返す
    public void reverseRightTop(int x, int y) {
        while(true) {
            //座標がボードの上端または右端に到達した場合の処理
            if(x > 7 || y < 2) {
                //コピーボードをリセットする
                copyBoard = CB.copySaveBoard(Main.board);
                break;
            }

            //隣が相手の色だった場合
            if(Main.board[x+1][y-1] == Main.enemyOsero) {
                //ひっくり返す座標をコピーボードに保存する
                copyBoard[x+1][y-1] = Main.userOsero;

            //隣が自分の色だった場合
            }else if(Main.board[x+1][y-1] == Main.userOsero){
                //変更内容をオリジナルのボードに反映する
                Main.board = CB.copySaveBoard(copyBoard);
                break;

            //隣が空白だった場合
            }else{
                //コピーボードをリセットする
                copyBoard = CB.copySaveBoard(Main.board);
                break;
            }
            x++;
            y--;
        }
    }

    public void reverseLeftBottom(int x, int y) {
        while(true) {

            //座標がボードの端に到達した場合の処理
            if(x < 2 || y >7) {
                //コピーボードをリセットする
                copyBoard = CB.copySaveBoard(Main.board);
                break;
            }

            //隣が相手の色だった場合
            if(Main.board[x-1][y+1] == Main.enemyOsero) {
                //ひっくり返す座標をコピーボードに保存する
                copyBoard[x-1][y+1] = Main.userOsero;

            //隣が自分の色だった場合
            }else if(Main.board[x-1][y+1] == Main.userOsero){
                //変更内容をオリジナルのボードに反映する
                Main.board = CB.copySaveBoard(copyBoard);
                break;

            //隣が空白だった場合
            }else{
                //コピーボードをリセットする
                copyBoard = CB.copySaveBoard(Main.board);
                break;
            }
            x--;
            y++;
        }
    }

    //右下をひっくり返す
    public void reverseRightBottom(int x, int y) {
        while(true) {

            //座標がボードの端に到達した場合の処理
            if(x > 7 || y > 7) {
                //コピーボードをリセットする
                copyBoard = CB.copySaveBoard(Main.board);
                break;
            }

            //隣が相手の色だった場合
            if(Main.board[x+1][y+1] == Main.enemyOsero) {
                //ひっくり返す座標をコピーボードに保存する
                copyBoard[x+1][y+1] = Main.userOsero;

            //隣が自分の色だった場合
            }else if(Main.board[x+1][y+1] == Main.userOsero){
                //変更内容をオリジナルのボードに反映する
                Main.board = CB.copySaveBoard(copyBoard);
                break;

            //隣が空白だった場合
            }else{
                //コピーボードをリセットする
                copyBoard = CB.copySaveBoard(Main.board);
                break;
            }
            x++;
            y++;
        }
    }
}

続いてMain.javaにコードを追記します。赤線を引いた箇所が追記箇所です

--Main.java--

package osero;

//省略

public class Main {

    //省略

    public static void main(String[] args) {

        //省略

        while(true) {
            //省略
        }

        while(true) {
            //省略
        }

        //入力した座標にオセロを置く
        RO.putOsero(inputX, inputY);

        //ボード情報をコピーボードにコピーする
        RO.makeSaveBoard(Main.board);

        //ひっくり返す処理
        RO.reverseLeft(inputX, inputY);
        RO.reverseRight(inputX, inputY);
        RO.reverseTop(inputX, inputY);
        RO.reverseBottom(inputX, inputY);
        RO.reverseLeftTop(inputX, inputY);
        RO.reverseRightTop(inputX, inputY);
        RO.reverseLeftBottom(inputX, inputY);
        RO.reverseRightBottom(inputX, inputY);

        //ボードを表示する
        SB.showBoard(board);
    }
}

次回

お疲れ様でした。今回は一番大変なオセロをひっくり返す処理を作成しました。

次回は、プレイヤー同士交互にオセロを置けるように、ターン制を実装したいと思います。

では、また会いましょう。バイバイ!

参考文献・おすすめ文献