#はじめに
javaを勉強する目的で制作してみました。いくつかのサイトを参考にしましたが、裏返すロジックの部分やmainメソッド内など自分で考えた部分が多いので、バグや不都合があるかもしれないです。
#ゲームの流れ
実行するとオセロが開始します。縦列、横列の順番で1~8までの整数を入力していきます。盤上の有効なマスに置くと白と黒のターンが変わり再び入力になります。すべてのマスに駒が置かれるか、白黒どちらか一色になった時点で勝敗を表示します。
#コード
##実行クラス
まずgameクラスのインスタンスを生成や変数の定義をします。そしてループ(各ターン)を開始して、正しい値の入力が確認された後その値と色(1なら黒2なら白)をturnOverメソッドに渡し裏返しの処理をします。裏返せたら色を交代して、駒の合計に1を足します。盤面に駒が埋まる=駒の数が64個、になったらループが終了します。そしてshowResultを呼び出し結果を表示します。
public class Othello {
public static void main(String[] args) {
// TODO 自動生成されたメソッド・スタブ
game game = new game();
int x = 0;
int y = 0;
//1なら黒、2なら白
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でループをスキップするのは3パターン。
下記の場合は色の交代と駒(ターン)のカウントを行わず再度入力になる。
1.整数以外が入力された場合→InputMismatchExceptionの例外を投げる
2.盤外である1~8の数字が入力された場合
3.turnOverメソッドを実行して、0が返ってきた場合→裏返せていない。ルール上置いてはいけない。
##gameクラス
ボード(盤面)の管理や表示、裏返しメソッドなどを記述。黒は1、白は2、空は0で管理します。
###メンバ変数とコンストラクタ
コンストラクタで初期の配置の挿入をします。
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配列をループでそれぞれ取り出します。さらに1マスずつループし、1なら●、2なら○、0なら□というようにlistに追加して1列ごと表示します。それと同時に色のカウントも行い、白黒の数を最後に表示します。
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方向分の)裏返すメソッドに渡します。1つも裏返せてない場合やもう駒が置かれている場合は0を、showBoredでカウントしてある色の数の片方が0になり決着がついた場合は-1を返すようになっています。
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から1ずつ引き・斜め左下ならxから1引きyに1を足す・・・という風にして調べていきます。次が相手の駒である限りマス目を移っていき、自分の駒にたどり着くまでループをします。途中でマスが空の場合や盤外になった場合は、何も裏返せていないため0を返します。自分の駒にたどり着いたら、そこの位置と置いた位置の差だけループして裏返し、その数を返します。
//上方向
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で数をカウントし直して、白黒比べて結果を表示します。
public void showResult() {
showBoard();
if (black < white) {
System.out.println("白の勝ち");
} else if (white < black) {
System.out.println("黒の勝ち");
} else {
System.out.println("引き分け");
}
}
#今後追加したいところ
1.対戦相手の制作
今は交互に入力して対戦してく形式ですが、一人でも使えるようにしたいです。
更新しました。javaでCUIオセロの制作その2
2.パス機能
オセロのルールで自分の駒をどこに置いても相手の駒が裏返せない場合(まだ自分の駒が残っていてパーフェクトではない)は、パスになり相手のターンになります。まだこの機能がなく、不完全なオセロなので追加したいです。
#参考にしたサイト
Javaでオセロゲームを作成を現役エンジニアが解説【初心者向け】
Javaで普通のオセロをつくった話
Javaでオセロを書いてみました。
スキャナで取得した値を指定したデータ型の値として取得する
ArrayListの使い方