はじめに
前回までの記事で見た目的にはビンゴゲームになってきたので、次の段階として、ビンゴゲームのリーチやビンゴの判定機能を作りたい。検索すれば答えがヒットするんだろうけど、自分で考え抜きたい意地が発現してしまったので、記事を書きながら頭を整理しつつ、作っていきたいと思う。
ふわっと作戦を考える
今ふわっと考えていることとしては、現状はカードに縦5マス並べたものを5列横に並べているのだが、これを二次元配列を使っての配置に変更すること。これで、縦横の判定はしやすくなるだろうと予想している。斜めに関しては、2パターンしかないし、個別に書き連ねるのが良いのかな?
まず二次元配列での配置で現状までの機能をつくる
コード
public class BingoGame extends JFrame implements ActionListener{
JButton mkCardBtn;
JPanel BingoCardPanel;
JPanel p1;
JPanel p2;
JPanel p3;
JPanel p4;
JPanel p5;
JLabel [] [] numLabel = new JLabel [5] [5];
LineBorder border = new LineBorder(Color.LIGHT_GRAY,2,true);
GridLayout gl = new GridLayout(5,1);
Container c;
JButton startBtn;
Random random = new Random();
JFrame gameFrame;
JTextArea numLog;
JButton spinBtn;
int spinNum;
ArrayList<Integer> numList1 = new ArrayList<Integer>();
ArrayList<Integer> numList2 = new ArrayList<Integer>();
ArrayList<Integer> numList3 = new ArrayList<Integer>();
ArrayList<Integer> numList4 = new ArrayList<Integer>();
ArrayList<Integer> numList5 = new ArrayList<Integer>();
ArrayList<Integer> spinNumbersList = new ArrayList<Integer>();
String reach = "リーチ";
String bingoStr = "ビンゴ!";
public static void main(String[] args) {
BingoGame frame = new BingoGame();
frame.setVisible(true);
}
public BingoGame() {
setBounds(10,10,600,400);
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mkCardBtn = new JButton("ビンゴカード作成");
mkCardBtn.addActionListener(this);
BingoCardPanel = new JPanel();
BingoCardPanel.setLayout(new GridLayout(1,5));
mkCard(p1,0,0,1);
mkCard(p2,0,1,6);
mkCard(p3,0,2,11);
mkCard(p4,0,3,16);
mkCard(p5,0,4,21);
JPanel p1_1 = new JPanel();
p1_1.setLayout(new GridLayout(1,2));
p1_1.add(mkCardBtn);
p1_1.add(BingoCardPanel);
startBtn =new JButton("ゲーム開始");
startBtn.addActionListener(this);
c = getContentPane();
c.add(p1_1, BorderLayout.CENTER);
c.add(startBtn, BorderLayout.SOUTH);
}
public void actionPerformed(ActionEvent e) {
if(e.getSource() == mkCardBtn) {
numList1.clear();
numList2.clear();
numList3.clear();
numList4.clear();
numList5.clear();
mkRandomNum(numList1,0);
}else if(e.getSource() == startBtn) {
insertGameFrame();
}
else if(e.getSource() == spinBtn) {
do {
spinNum = random.nextInt(75) + 1;
}while(spinNumbersList.indexOf(spinNum) != -1);
spinNumbersList.add(spinNum);
String spinNumber = Integer.toString(spinNum);
numLog.append(spinNumber + ",");
}
punchHole();
}
public void mkRandomNum(ArrayList<Integer> al, int i) {
int x = 0;
int y = 0;
for(y = 0; y < 5; y++) {
switch(y) {
case 0:
al = numList1;
i = 1;
break;
case 1:
al = numList2;
i = 16;
break;
case 2:
al = numList3;
i = 31;
break;
case 3:
al = numList4;
i = 46;
break;
case 4:
al = numList5;
i = 61;
break;
}
while(al.size() < 5) {
int num = random.nextInt(15) + i;
if(al.indexOf(num) == -1) {
al.add(num);
}
}
for(x = 0; x < 5; x++) {
String number = Integer.toString(al.get(x));
numLabel[x][y].setText(number);
}
}
}
public void mkCard(JPanel p, int x, int y, int i) {
p = new JPanel();
p.setLayout(gl);
for(x = 0; x < 5; x++) {
String testNum = Integer.toString(x + i);
numLabel[x][y] = new JLabel(testNum);
numLabel[x][y].setBorder(border);
numLabel[x][y].setOpaque(true);
p.add(numLabel[x][y]);
}
BingoCardPanel.add(p);
}
public void insertGameFrame() {
gameFrame = new JFrame();
gameFrame.setBounds(100, 100, 600, 400);
gameFrame.setLocationRelativeTo(null);
gameFrame.setDefaultCloseOperation(EXIT_ON_CLOSE);
numLog = new JTextArea(5,10);
LineBorder black = new LineBorder(Color.black,2,true);
numLog.setBorder(black);
numLog.setEditable(false);
numLog.setLineWrap(true);
JPanel panel1 = new JPanel();
GridLayout gridlayout = new GridLayout(1,2);
gridlayout.setHgap(5);
gridlayout.setVgap(5);
panel1.setLayout(gridlayout);
panel1.add(BingoCardPanel);
panel1.add(numLog);
spinBtn = new JButton("Spin");
spinBtn.addActionListener(this);
Container c2 = gameFrame.getContentPane();
c2.add(panel1,BorderLayout.CENTER);
c2.add(spinBtn, BorderLayout.SOUTH);
gameFrame.setVisible(true);
}
public void punchHole() { //カードに穴あける。
if(spinNum <= 15) {
changeBackgroundColor(numList1, 0);
}else if(spinNum <= 30) {
changeBackgroundColor(numList2, 1);
}else if(spinNum <= 45) {
changeBackgroundColor(numList3, 2);
}else if(spinNum <= 60) {
changeBackgroundColor(numList4, 3);
}else if(spinNum <= 75) {
changeBackgroundColor(numList5, 4);
}
}
public void changeBackgroundColor(ArrayList<Integer> al, int y) { //背景色を変える
int x = al.indexOf(spinNum);
if(x != -1) {
numLabel[x][y].setBackground(Color.red);
}
}
}
ちょっと説明
変えたのは
- mkCard()
- mkRandomNum()
- punchHole()
- changeBackgroundColor()
の4つ。引数は書くのめんどくさかったので、省略。
まず1つ目のmkCard()
について。
今までは5つのJLabel[]を使っていたところを、1つのJLabel[][]と二次元配列に変更した。なのでxとyを使ってやってみた。
二重にfor文を書いてxとyの値を変化させれば仮引数が要らなくなるかもしれない。。と後から思った。
次に2つ目のmkRandomNum()
について。
これは大きいfor文でyの値を変化させつつ、中の小さいfor文でxとかの値を変えている。最初はswitch文でarraylistを切り替えるのではなく、配列にarrayListを入れようとしていたのだが、Eclipseに総称型ArrayListの配列は作れないと怒られてしまったので、switch文でyの値に応じてarrayListを切り替えられるようにした。
3つ目のpunchHole()
は、changeBackgroundColor()
に渡す引数を変更しただけ。
最後に4つ目のchangeBackgroundColor()
について
これも最初のmkCard()
と同じく、JLabel[]だったところをJLabel[][]に変えた。
透明非透明を設定するsetOpaque()はmkCard()
内でtrueに設定している。
それともう一つだけ。spinBtnの処理を書いてるところのdo~while文の使い方を間違えていたので修正しました!これで本当に数字被りが無くなったはず。
リーチ&ビンゴ判定機能をつくる
いよいよ本命の機能作成にとりかかる。
追加したのはこちらのコード。
public void judge(int x, int y) {
//たてスキャン
int a = x;
int b = y;
String str = numLabel[a][b].getText();
int jnum = Integer.parseInt(str);
int c_count = 0;
int r_count = 0;
for(int i=0;i<5;i++) {
//numLabelに載ってる数字とspinNumbersListの中身を比べる。
str = numLabel[a][i].getText();
jnum = Integer.parseInt(str);
if(spinNumbersList.indexOf(jnum) != -1) {
c_count++;
}
}
if(c_count == 4) {
numLog.append(reach);
}else if(c_count == 5) {
numLog.append(bingoStr);
}
//横スキャン
for(int i=0;i<5;i++) {
str = numLabel[i][b].getText();
jnum = Integer.parseInt(str);
if(spinNumbersList.indexOf(jnum) != -1) {
r_count++;
}
}
if(r_count == 4) {
numLog.append(reach);
}else if(r_count == 5) {
numLog.append(bingoStr);
}
}
public void crossJudge() {
String str;
int jnum;
int count1 = 0;
int count2 = 0;
for(int i=0;i<5;i++) {//左上から右下
str = numLabel[i][i].getText();
jnum = Integer.parseInt(str);
if(spinNumbersList.indexOf(jnum) != -1) {
count1++;
}
}
if(count1 == 4) {
numLog.append(reach);
}else if(count1 == 5) {
numLog.append(bingoStr);
}
for(int i=0;i<5;i++) {//右上から左下
int j = 4-i;
str = numLabel[i][j].getText();
jnum = Integer.parseInt(str);
if(spinNumbersList.indexOf(jnum) != -1) {
count2++;
}
if(count2 == 4) {
numLog.append(reach);
}else if(count2 == 5) {
numLog.append(bingoStr);
}
}
}
このふたつのメソッドを背景色変えるchangeBackgroundColor()
の中に追加した。
縦横スキャンに関しては、穴開けたマスの座標を使って、x座標だけ増減させるなどして、縦横の列をスキャンして、spinNumbersListに入っている数字と各マスの数字が一致しているか調べてる。4つ一致でリーチ表示、5つ一致でビンゴ表示をnumLogに出している。
ただ、この方法だと、同じリーチやビンゴを複数回表示することがあること、ダブルリーチやダブルビンゴなどには対応していないことが問題点。
だけど、自分の力だけだとこれが限界そうです。
斜め列の判定について
左上から右下への斜めは、座標がxとyで等しいので、
numLabel[i][i]でiを位置ずつ増やしスキャンした。
反対の、右上から左下への斜めは、yの値を一次関数を使って表してみた。
実行結果
終わりに
完璧ではないけど、自分が当初作ろうとしていた昨日は作ることができた。良い達成感。
今後は自分のコードを改良したり、他の人のビンゴコードを見て勉強してみようと思う。自分はJavaで書いてるのに、オブジェクト指向の便利機能をほとんど使えてないから、ここも勉強。