前回でビンゴゲームの基本的な仕組みを作ることができた。今回はそれを応用してケータイショップでおなじみの出てきた番号を消していくビンゴを作成してみよう。
実行例
34 51 46 9 58
14 7 6 27 56
41 43 . 54 59
10 15 12 61 50
53 55 4 44 24
実行すると5*5のビンゴカードが表示される。真ん中は最初から開けられている
34 51 46 9 58
14 7 6 27 56
41 43 . 54 59
10 15 12 61 50
53 55 4 44 24
Turn:1
51 HIT!
---------------
34 . 46 9 58
14 7 6 27 56
41 43 . 54 59
10 15 12 61 50
53 55 4 44 24
---------------
エンターキーを押すと1つ目のボールが出てくる。今回はこの番号(51)があったのでHIT!と表示されその番号が消える
Turn:33
70
---------------
. . . . 58
14 7 6 27 56
41 . . 54 .
. . . . 50
53 55 4 44 24
---------------
Turn:34
50 HIT!
1 Line BINGO!!
---------------
. . . . 58
14 7 6 27 56
41 . . 54 .
. . . . .
53 55 4 44 24
---------------
途中は省略されているが34回目に50が出てBINGOとなった。(1列揃った)
列が揃ったらゲーム終了だ。
作成
メソッド
まずはいつものように処理に必要そうなメソッドを作成していく
static int[] makeNums(int max){
int[] nums=new int[max];
for(int i=0;i<max;i++){
nums[i]=i+1;
}
return nums;
}
今回はボールにもカードにも1~75のような連番が必要となる。
引数に個数を受け取ると1からの連番の配列を返却するメソッドを作成する。
static void arrShuffle(int[] nums){
Random rand=new Random();
for(int i=0;i<nums.length-1;i++){
int index=rand.nextInt(nums.length-i);
int temp=nums[index];
nums[index]=nums[nums.length-1-i];
nums[nums.length-1-i]=temp;
}
}
今回はカードについても、ボールについてもランダムに並べる処理が必要となる。
引数に受け取った配列をシャッフルする処理を作成しよう。
これは自分で1から考えることではない。
理屈を理解したらいつでも使い回せるようにしておこう。
(配列シャッフルのアルゴリズム)
static int[] createBalls(int max){
int[] balls=makeNums(max);
arrShuffle(balls);
return balls;
}
最初につくった2つのメソッドを使って1〜75の番号のついたボールを作成しランダムに並び替えている{4,1,5,43,71,…..}こうしておいて先頭から取り出していけば重複して同じボールを引いてしまうことを避けられる。
static int[][] makeCard(int width,int max){
int[] nums=makeNums(max);
arrShuffle(nums);
int[][] card=new int[width][width];
for(int i=0;i<card.length;i++){
for(int j=0;j<card[i].length;j++){
card[i][j]=nums[i*width+j];
}
}
return card;
}
最初に使った2つのメソッドを使って1~75までの数字をランダムに並べ順番に2次元配列に詰めていっている。
これがビンゴカードとなる。
static void showCard(int[][] card){
for(int[] line:card){
for(int n:line){
String s=n==0? " .":String.valueOf(n);
System.out.printf("%3s",String.valueOf(s));
}
System.out.println();
}
}
ビンゴカード配列を実行例にあわせて出力するメソッドだ。
今回は拡張for文で内容にアクセスしている。
0が入っているところをドットで表現している。今回はボールがヒットしたらそこに0を入れていくことにする。
static boolean isSame(int[] line){
int first=line[0];
for(int i=1;i<line.length;i++){
if(first != line[i]){
return false;
}
}
return true;
}
引数に1次元配列を受け取り、その要素がすべて同じ場合にはtrueをそうでない場合はfalseを返却するメソッドだ。最初の一つと違うのを見つけた段階でfalseを返却している。もしfalseにならなかったということは全部同じということになる。
static int countLine(int[][] temp){
int count=0;
for(int[] line:temp){
if(isSame(line)){count++;}
}
return count;
}
2次元配列を引数に受け取り、その行がすべて同じ場合だった場合にcountを1増やしている。
例)
{ {0,0,0,0,58}, {14,7,6,27,56}, {41,0,0,54,0} {0,0,0,0,0} {53,55,4,44,24} }
上記の2次元配列の場合は1を返す。
static int horizontalMatchLine(int[][] card){
return countLine(card);
}
横のラインの判定は簡単だ。最初に作った2次元配列をそのままcountLineにわたしてあげれば何列揃ったのかがわかる。
static int verticalMatchLine(int[][] card){
int[][] lines=new int[card.length][card.length];
for(int i=0;i<card.length;i++){
for(int j=0;j<card.length;j++){
lines[i][j]=card[j][i];
}
}
return countLine(lines);
}
もともとのビンゴカード配列を引数に受け取り、それを元に縦の組み合わせを取得している。
元となる配列 { {2,2,2}, {3,1,5}, {4,4,4} } 新たに作成される配列 { {2,3,4}, {2,1,4}, {2,5,4} }
コードのなかにあるiとjを入れ替える処理はこういったときの常套手段だ。
処理の流れを目で追って上の図になることを確認してもらいたい。
この配列ができればあとはcountLineに渡せば縦のラインが何列そろっているかがわかる。
static int crossMatchLine(int[][] card){
int[][] lines=new int[2][card.length];
for(int i=0;i<lines.length;i++){
for(int j=0;j<card.length;j++){
if(i==0){
lines[i][j]=card[j][j];
}else{
lines[i][j]=card[j][card.length-1-j];
}
}
}
return countLine(lines);
}
最後に斜めのラインを調べるメソッドを作成する。
斜めのラインは2つあるので要素数2の2次元配列を作成する。
最初の要素は{0,0}{1,1}{2,2}といった要素を取得していけばよいので[j][j]となる。
次の要素は{0,2},{1,1},{2,0}といった要素なので[j][card.length-1-j]となる。card.lengthが3と仮定して組み合わせが取得できることを確認してもらいたい。
これで斜めの2つを要素にもつ2次元配列ができたのであとはcountLineに渡すだけだ。これで斜めのラインが何列そろったのかがわかる。
static boolean isHit(int[][] card,int ball){
for(int i=0;i<card.length;i++){
for(int j=0;j<card[i].length;j++){
if(card[i][j]==ball){
card[i][j]=0;
return true;
}
}
}
return false;
}
カード配列と出た番号を引数で受け取って出目がカードにあった場合はそこに0を代入し、出目があった場合はtrueをそうでない場合はfalseを返却するメソッド。
static int totalCountLine(int[][] card){
int count=horizontalMatchLine(card)
+verticalMatchLine(card)
+crossMatchLine(card);
return count;
}
横、縦、斜めのBingoになったライン集計するメソッド。上記で作ったメソッドを順番に呼んでいるだけだ。
static void printLine(int width){
for(int i=0;i<width*3;i++){
System.out.print('-');
}
System.out.println();
}
実行例にあわせて罫線を引くメソッドも作っておこう。ビンゴカードの幅に罫線も連動するようにしておこう。
メインメソッド
上記で必要な処理が全て揃った。後はメインメソッドで実行例のようになるようにしていこう。
全ソースコード
import java.util.*;
public class BingoGame{
public static void main(String[] args){
final int WIDTH=5,MAX_NUMBER=75;
Scanner sc=new Scanner(System.in);
int[][] card=makeCard(WIDTH,MAX_NUMBER);
int center=WIDTH/2;
card[center][center]=0;
showCard(card);
int[] balls=createBalls(MAX_NUMBER);
for(int i=0;;i++){
sc.nextLine();
System.out.println("Turn:"+(i+1));
System.out.print(balls[i]);
int bingoCount=0;
if(isHit(card,balls[i])){
System.out.print(" HIT!");
bingoCount=totalCountLine(card);
if(bingoCount>0){
System.out.print("\n"+bingoCount+" Line BINGO!!");
}
}
System.out.println();
printLine(WIDTH);
showCard(card);
printLine(WIDTH);
if(bingoCount>0){
return;
}
}
}
static int[] makeNums(int max){
int[] nums=new int[max];
for(int i=0;i<max;i++){
nums[i]=i+1;
}
return nums;
}
static int[][] makeCard(int width,int max){
int[] nums=makeNums(max);
arrShuffle(nums);
int[][] card=new int[width][width];
for(int i=0;i<card.length;i++){
for(int j=0;j<card[i].length;j++){
card[i][j]=nums[i*width+j];
}
}
return card;
}
static void arrShuffle(int[] nums){
Random rand=new Random();
for(int i=0;i<nums.length-1;i++){
int index=rand.nextInt(nums.length-i);
int temp=nums[index];
nums[index]=nums[nums.length-1-i];
nums[nums.length-1-i]=temp;
}
}
static void showCard(int[][] card){
for(int[] line:card){
for(int n:line){
String s=n==0? " .":String.valueOf(n);
System.out.printf("%3s",String.valueOf(s));
}
System.out.println();
}
}
static int[] createBalls(int max){
int[] balls=makeNums(max);
arrShuffle(balls);
return balls;
}
static boolean isHit(int[][] card,int ball){
for(int i=0;i<card.length;i++){
for(int j=0;j<card[i].length;j++){
if(card[i][j]==ball){
card[i][j]=0;
return true;
}
}
}
return false;
}
static int totalCountLine(int[][] card){
int count=horizontalMatchLine(card)
+verticalMatchLine(card)
+crossMatchLine(card);
return count;
}
static boolean isSame(int[] line){
int first=line[0];
for(int i=1;i<line.length;i++){
if(first != line[i]){
return false;
}
}
return true;
}
static int horizontalMatchLine(int[][] card){
return countLine(card);
}
static int verticalMatchLine(int[][] card){
int[][] lines=new int[card.length][card.length];
for(int i=0;i<card.length;i++){
for(int j=0;j<card.length;j++){
lines[i][j]=card[j][i];
}
}
return countLine(lines);
}
static int crossMatchLine(int[][] card){
int[][] lines=new int[2][card.length];
for(int i=0;i<lines.length;i++){
for(int j=0;j<card.length;j++){
if(i==0){
lines[i][j]=card[j][j];
}else{
lines[i][j]=card[j][card.length-1-j];
}
}
}
return countLine(lines);
}
static int countLine(int[][] temp){
int count=0;
for(int[] line:temp){
if(isSame(line)){count++;}
}
return count;
}
static void printLine(int width){
for(int i=0;i<width*3;i++){
System.out.print('-');
}
System.out.println();
}
}
以上で終了だ。全3回、ビンゴのお題を通して2次元配列やメソッドの演習を行った。
コメント