めちゃくちゃ簡易のビンゴゲームを作る　＃３

# はじめに
[前回までの記事](https://qiita.com/snow_saboten/items/cb3fd3af99575f12eeee)で見た目的にはビンゴゲームになってきたので、次の段階として、ビンゴゲームのリーチやビンゴの判定機能を作りたい。検索すれば答えがヒットするんだろうけど、自分で考え抜きたい意地が発現してしまったので、記事を書きながら頭を整理しつつ、作っていきたいと思う。

# ふわっと作戦を考える
今ふわっと考えていることとしては、現状はカードに縦５マス並べたものを5列横に並べているのだが、これを二次元配列を使っての配置に変更すること。これで、縦横の判定はしやすくなるだろうと予想している。斜めに関しては、2パターンしかないし、個別に書き連ねるのが良いのかな？

# まず二次元配列での配置で現状までの機能をつくる
## コード
```Java
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()`について。
今までは５つの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文の使い方を間違えていたので修正しました！これで本当に数字被りが無くなったはず。
# リーチ＆ビンゴ判定機能をつくる
いよいよ本命の機能作成にとりかかる。
追加したのはこちらのコード。
```Java
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に入っている数字と各マスの数字が一致しているか調べてる。４つ一致でリーチ表示、５つ一致でビンゴ表示をnumLogに出している。
ただ、この方法だと、同じリーチやビンゴを複数回表示することがあること、ダブルリーチやダブルビンゴなどには対応していないことが問題点。
だけど、自分の力だけだとこれが限界そうです。

斜め列の判定について
左上から右下への斜めは、座標がxとyで等しいので、
numLabel[i][i]でiを位置ずつ増やしスキャンした。
反対の、右上から左下への斜めは、yの値を一次関数を使って表してみた。

## 実行結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2586452/1aeb4446-6bca-ed32-9cd8-e28481aee620.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2586452/bff51ade-76c6-37c0-0fec-cfd065194a19.png)
# 終わりに
完璧ではないけど、自分が当初作ろうとしていた昨日は作ることができた。良い達成感。
今後は自分のコードを改良したり、他の人のビンゴコードを見て勉強してみようと思う。自分はJavaで書いてるのに、オブジェクト指向の便利機能をほとんど使えてないから、ここも勉強。