Làm Game Dò Mìn Bằng Java (Phần 1)

Làm Game Dò Mìn Bằng Java (Phần 1)

Trò chơi dò mìn là một trò chơi rất quen thuộc với mọi người, trò chơi đòi hỏi người chơi phải có suy luận chính xác, khả năng ghi nhớ, và đôi khi cũng cần một chút may mắn.

Giao diện của trò chơi bao gồm các ô vuông, tại mỗi ô có thể có bom hoặc không, ngoài ra ô các ô vuông có thể có các con số từ 0 đến 8 chính là số lượng mìn xung quanh ô vuông đó. (Như hình vẽ bên dưới thì trong 8 ô vuông màu xanh sẽ có đúng N quả mìn.

Luật chơi của trò chơi khá đơn giản, ở mỗi ô trống sẽ có mìn hoặc không. Khi người chơi mở một ô vuông, nếu ô vuông đó có mìn thì người chơi sẽ bị thua. Nếu bạn suy luận được rằng một ô vuông nào đó chắc chắn có mìn hãy gán cờ và ô vuông đó. 

Bạn sẽ chiến thắng khi bạn gắn được tất cả các lá cờ vào đúng chính xác từng quả mìn, nên mỗi khi gắn cờ, bạn phải chắc chắn rằng ô vuông đó là một quả mìn.

Khởi tạo dữ liệu cần thiết.

1. Dữ liệu biểu diễn các ô vuông.

Ta sẽ dùng một mảng hai chiều kiểu integer để lưu các giá trị trong các ô vuông, những ô nào có mìn ta sẽ gán giá trị nó bằng -1.

private int values[][] = new int[maxXY][maxXY];

Ví dụ:

Cách khởi tạo mảng value:

Đầu tiên chúng ta cần khởi tạo các ô giá trị -1 trước, sau sẽ tạo giá trị của các ô khác bằng các đếm các giá trị -1 xung quanh nó. Chúng ta sẽ sử dụng hàm random() để khởi tạo mìn.

Tuy nhiên, hàm random() là hàm khởi tạo ngẫu nhiên, nên việc tạo mìn sẽ rất đều (các quả mìn gần như sẽ được phân đều trên giao diện).

	public void initBom() {
		int i, j, dem = 0;
		for (i = 0; i <= m + 1; i++)
			for (j = 0; j <= n + 1; j++)
				values[i][j] = 0;
		while (dem < BOM) {
			do {
				i = (int) ((m - 1) * Math.random()) + 1;
				j = (int) ((n - 2) * Math.random()) + 1;
			} while (values[i][j] != 0);
			if (values[i][j] == 0) {
				init(i, j);
			}
		}
	}

Nếu như mình khởi tạo như trên thì giao diện sẽ như thế này.

Việc khởi tạo mảng như trên sẽ làm game quá dễ, nên chúng ta sẽ dùng một cách khởi tạo khác để tăng độ khó cho game.

Các quả mìn cần được sắp xếp thành từng nhóm, để làm được điều đó ta có thể làm như sau:

	public void initBom() {
		float ratio = (float) 0.05; // Tỉ lệ tạo mìn những ô xung quanh
		int i, j;
		for (i = 0; i <= m + 1; i++)
			for (j = 0; j <= n + 1; j++)
				values[i][j] = 0;
		while (dem < BOM) {
			// Chọn ra tạo độ chưa phải là mìn.
			do {
				i = (int) ((m - 1) * Math.random()) + 1;
				j = (int) ((n - 2) * Math.random()) + 1;
			} while (values[i][j] != 0);
			if (values[i][j] == 0) 
				init(i, j, ratio);
		}
	}
	public void init(int i, int j, float ratio) {
		if (Math.random() < ratio) {
			values[i][j] = -1;
			for (int k = i - 1; k <= i + 1; k++)
				for (int h = j - 1; h <= j + 1; h++)
					if (values[k][h] != -1) {
						values[k][h]++;
					}
			dem++;
			//Gọi hàm tạo bom cho những ô xung quanh.
			for (int k = i - 1; k <= i + 1; k++)
				for (int h = j - 1; h <= j + 1; h++)
					if (k > 0 && h > 0 && values[k][h] != -1 && dem < BOM)
						init(k, h, ratio);
		}
	}

Nghĩa là khi ta khởi tạo ô vuông (i, j) chứa bom thì sẽ có tỉ lệ ratio khởi tạo những ô xung quanh nó cũng là bom, việc khởi tạo này ta dùng phương pháp đệ quy để tiếp tục gọi hàm khởi tạo cho những ô xung quanh nó.

Trong đoạn code trên của mình thì có ratio = 0.05, nghĩa là xung quanh mỗi ô có bom thì xác suất mỗi ô trong 8 ô vuông xung quanh nó chỉ là 5%, tuy chỉ 5% thôi nhưng nó không hề rời rạc như code ban đầu đâu nha, nếu ta tính xác suất 8 ô xung quanh nó có bom thì sẽ được 33,66% đấy, các bạn có thể tăng chỉ số ratio lớn hơn để tăng độ khó của game.

Bên dưới chính là một cách phân bố của bom khi ta tạo theo cách trên:

2. Mảng đánh dấu.

Trong trò chơi này khi chúng ta mở hay gắn cờ trên một ô vuông thì ô vuông đó khi được mở nữa, vì vậy ta cần một mảng để đánh dấu cách ô vuông chưa mở.

	private boolean tick[][] = new boolean[maxXY][maxXY];

Với tick[i][j] bằng true nếu ô (i, j) có thể mở.

3. Kích thước và số lượng Bom.

Với trò chơi dò mìn thì chúng ta cần khởi tạo 2 giá trị là số hàng số cột của ma trận, và một số lượng Bom của màn chơi đó, mình sẽ chia game này thành 3 mức độ là dễ, trung bình và khó. Kích thước cũng như số lương Bom cũng sẽ được tăng dần.

	int M[] = {8, 15, 21};
	int N[] = {10, 19, 27};
	int Mines[] = {10, 40, 100};

Với M[k]N[k] là kích thước của ma trận và Mines[k] là số lương bom, k = 0 nếu ở mức độ dễ, k = 1 ở mức độ trung bình và k = 2 ở mức độ khó.

Thiết lập giao diện.

Giao diện bài này cũng khá đơn giản, chỉ bao gôm một mảng 2 chiều bào gồm của JButton.

	private JButton bt[][] = new JButton[maxXY][maxXY];

Lưu ý rằng phần text của mỗi Button các bạn có thể để rỗng hoặc các ký tự <space>, miễn sao tất cả các ô đều giống nhau.

Trong Java Swing để lấy sự kiện khi ta click và một Button nó thông qua một biến e có kiểu ActionEvent, tuy nhiên từ đó ta chưa thể biết được tạo độ của Button đó, vì tất cả Button đều giống nhau, các bạn có thể phân biệt các Button qua ID.

Các mình thường dùng cho những bài dạng như này là mình thay đổi ActionCommand của Button thành dạng chuỗi "Tạo_độ_x Tạo độ_y". sau đó ta có thể tách nó ra khi ta click và Button.

		for (int i = 1; i <= m; i++)
			for (int j = 1; j <= n; j++){
				bt[i][j] = new JButton("   ");
				bt[i][j].setActionCommand(i + " " + j);
			}

Khi click và Button ta sẽ tách tạo độ của nó như sau:

			String s = e.getActionCommand();
			int k = s.indexOf(32);
			i = Integer.parseInt(s.substring(0, k));
			j = Integer.parseInt(s.substring (k + 1, s.length()));

Tạm kết.

Trên đây là cách mình tạo giao diện cho trò chơi dò mìn cũng như đưa ra một số dữ liệu quan trọng, cần thiết cho game. Bài blog mình sẽ hướng dẫn các bạn viết tất của các phương thức xử lý để của h trò chơi này nhé, nếu các bạn có thắc mắc hay góp ý gì, vui lòng để lại dưới phần bình luận. Hẹn gặp các bạn trong phần tiếp sau nha.

Video demo game: