javaでCUIオセロの制作

#はじめに
javaを勉強する目的で制作してみました。いくつかのサイトを参考にしましたが、裏返すロジックの部分やmainメソッド内など自分で考えた部分が多いので、バグや不都合があるかもしれないです。
#ゲームの流れ
実行するとオセロが開始します。縦列、横列の順番で１～８までの整数を入力していきます。盤上の有効なマスに置くと白と黒のターンが変わり再び入力になります。すべてのマスに駒が置かれるか、白黒どちらか一色になった時点で勝敗を表示します。

![スクリーンショット (24).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/685795/f3b9506b-6880-65ac-63af-a4e91e9b080c.png)
![スクリーンショット (23).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/685795/1c1b14ba-8ef7-9540-2d49-7642e207261a.png)



#コード
##実行クラス
まずgameクラスのインスタンスを生成や変数の定義をします。そしてループ（各ターン）を開始して、正しい値の入力が確認された後その値と色（１なら黒２なら白）をturnOverメソッドに渡し裏返しの処理をします。裏返せたら色を交代して、駒の合計に１を足します。盤面に駒が埋まる＝駒の数が64個、になったらループが終了します。そしてshowResultを呼び出し結果を表示します。

```java:Othello.java
public class Othello {

	public static void main(String[] args) {
		// TODO 自動生成されたメソッド・スタブ
		
		game game = new game();
		
		int x = 0;
		int y = 0;
		
		//１なら黒、２なら白
		int num = 1;
		
		//ボードの駒の数
		int komaTotal = 4;
		
		Scanner sc = new Scanner(System.in);
		
		//ここで終了判定
		while(komaTotal < 64) {
			
			//整数入力、それ以外の場合の例外
		    try{
		    	System.out.println("行を入力してください。");
		    	y = sc.nextInt();
		    	System.out.println("列を入力してください。");
		    	x = sc.nextInt();
		    }catch (InputMismatchException e){
		        System.out.println("整数を入力してください");
		        sc.next();
		        continue;
		    }
		    
		    //盤上か(1～8)
		    if (x <= 0 || x > 8) {
		    	System.out.println("盤上に置いてください");
		    	continue;
		    } else if (y <= 0 || y > 8) {
		    	System.out.println("盤上に置いてください");
		    	continue;
		    }
		    
			//メソッドの実行  裏返した駒の合計
			int sumReverse = game.turnOver(y - 1, x - 1, num);
			
			//0なら裏返せてない＝無効な場所
			//-1ならどちらかが一色になって決着
			if (sumReverse == 0) {
				System.out.println("有効な場所に置いてください");
				continue;
			} else if (sumReverse == -1) {
				break;
			}
			
			//交代
			if (num == 1) {
				num = 2;
			} else if (num == 2) {
				num = 1;
			}
			
			komaTotal++;
			
		}
		
		sc.close();
		System.out.println("終了");
		
		game.showResult();

	}
}
```

**continueでループをスキップするのは３パターン。**
下記の場合は色の交代と駒（ターン）のカウントを行わず再度入力になる。
1.整数以外が入力された場合→InputMismatchExceptionの例外を投げる
2.盤外である１～８の数字が入力された場合
3.turnOverメソッドを実行して、０が返ってきた場合→裏返せていない。ルール上置いてはいけない。


##gameクラス
ボード（盤面）の管理や表示、裏返しメソッドなどを記述。黒は１、白は２、空は０で管理します。

###メンバ変数とコンストラクタ
コンストラクタで初期の配置の挿入をします。

```java:game.java
private int[][] bored = new int[8][8];
	
private int white = 0;
private int black = 0;

private int colour = 0;

//コンストラクタ
public game() {
    
    //初期配置。[3][3],[4][4]は白、[3][4],[4][3]は黒
    bored[3][4] = 1;
    bored[4][3] = 1;
    bored[3][3] = 2;
    bored[4][4] = 2;
    
    showBoard();
}
````
###ボードの表示
盤上の駒を表示します。bored配列をループでそれぞれ取り出します。さらに１マスずつループし、１なら●、２なら○、0なら□というようにlistに追加して１列ごと表示します。それと同時に色のカウントも行い、白黒の数を最後に表示します。

```java:game.java
public void showBoard() {
    
    white = 0;
    black = 0;
    
    //arrayListに数字と対応する駒を入れて表示と各色のカウント
    for (int[] nums : bored) {
        
        ArrayList<String> list = new ArrayList<>();
        
        for (int a : nums) {
            if (a == 0) {
                list.add("□");
            } else if (a == 1) {
                list.add("●");
                black++;
            } else if (a == 2) {
                list.add("◯");
                white++;
            }
        }
        
        System.out.println(list);
        
    }

    System.out.println("黒：" + black + "個");
    System.out.println("白：" + white + "個");
}

```
###ターン
入力された位置のy,x座標の数字を9個の（9方向分の）裏返すメソッドに渡します。１つも裏返せてない場合やもう駒が置かれている場合は０を、showBoredでカウントしてある色の数の片方が０になり決着がついた場合は-1を返すようになっています。

```java:game.java
public int turnOver(int y, int x, int num) {
    
    //裏返した駒の合計
    int sumReverse = 0;
    //自分の色（数字）のセット
    colour = num;
    
    //置く場所が空なら
    if (bored[y][x] == 0) {
        
        sumReverse += turnUp(y,x);
        sumReverse += turnDown(y,x);
        sumReverse += turnLeft(y,x);
        sumReverse += turnRight(y,x);
        sumReverse += turnRightUp(y,x);
        sumReverse += turnRightDown(y,x);
        sumReverse += turnLeftUp(y,x);
        sumReverse += turnLeftDown(y,x);
        
    }
    
    showBoard();
    
    //どちらか一色になったら
    if (white == 0 || black == 0) {
        return -1;
    }
    
    return sumReverse;
    
}
```

###裏返し
全方向だと長くなってしまうので、例として上方向と斜め左下を裏返すものを載せました。駒が置かれた位置が引数のyとxになります。それぞれの方向の駒を調べたいため、上方向ならyから１ずつ引き・斜め左下ならxから１引きyに１を足す・・・という風にして調べていきます。次が相手の駒である限りマス目を移っていき、自分の駒にたどり着くまでループをします。途中でマスが空の場合や盤外になった場合は、何も裏返せていないため０を返します。自分の駒にたどり着いたら、そこの位置と置いた位置の差だけループして裏返し、その数を返します。

```java
//上方向
public int turnUp(int y, int x) {
		
    //次のマスのインデックスへ
    int targetY = y - 1;
    
    //次のマス目が盤上にあるか
    if(targetY < 0) {
        return 0;
    }
    
    //相手の駒である限りループ（空または自分の色でない）
    while(bored[targetY][x] != 0 && bored[targetY][x] != colour) {
        
        //次のマスへ
        targetY--;
        
        //盤外なら終了
        if(targetY < 0) {
            break;
        }
        
        //自分の駒までたどり着いたら
        if (bored[targetY][x] == colour) {
            
				//自分の色にした駒の数
				int count = 0;
				
				//置いた位置からたどり着いた自分の駒の分ループして色を変える
				for (; targetY < y; y--) {
					
					bored[y][x] = colour;
					count++;
				}
				
				//置いた駒を引いて、裏返した相手の駒の数を返す
				return count - 1;
        }
        
    }
    
    return 0;
    
}

//斜め左下
public int turnLeftDown(int y, int x) {
		
    int targetX = x - 1;
    int targetY = y + 1;
    
    if (targetX < 0 || targetY > 7) {
        return 0;
    }
    
    while(bored[targetY][targetX] != 0 && bored[targetY][targetX] != colour) {
        
        targetX--;
        targetY++;
        
        if (targetX < 0 || targetY > 7) {
            break;
        }
        
        if (bored[targetY][targetX] == colour) {
            
				int count = 0;
				
				for (;targetX < x; x--, y++) {
					
					bored[y][x] = colour;
					count++;
				}
				
				return count - 1;
        }
        
    }
    
    return 0;
    
}
```
###結果の表示
showBoredで数をカウントし直して、白黒比べて結果を表示します。

```java:game.java
public void showResult() {
    
    showBoard();
    
    if (black < white) {
        System.out.println("白の勝ち");
    } else if (white < black) {
        System.out.println("黒の勝ち");
    } else {
        System.out.println("引き分け");
    }
    
}
```

#今後追加したいところ
1.**対戦相手の制作**
~~今は交互に入力して対戦してく形式ですが、一人でも使えるようにしたいです。~~
更新しました。[javaでCUIオセロの制作その２](https://qiita.com/ymt8781/items/20cc0e70380fd9fc16f0)

2.**パス機能**
オセロのルールで自分の駒をどこに置いても相手の駒が裏返せない場合（まだ自分の駒が残っていてパーフェクトではない）は、パスになり相手のターンになります。まだこの機能がなく、不完全なオセロなので追加したいです。

#参考にしたサイト
[Javaでオセロゲームを作成を現役エンジニアが解説【初心者向け】](https://techacademy.jp/magazine/22228)
[Javaで普通のオセロをつくった話](https://qiita.com/yonedaco/items/1dde224740885997d679)
[Javaでオセロを書いてみました。](https://qiita.com/nogitsune413/items/d252a879480480a0697a)  
[スキャナで取得した値を指定したデータ型の値として取得する](https://www.javadrive.jp/start/scanner/index2.html)
[ArrayListの使い方](https://www.javadrive.jp/start/collection/index1.html)