实现人人对战的五子棋并且可以判断输赢。
1.界面
public class GoBangUI1 extends JFrame {
String[] s = {"悔棋","开始"};
BangListener bl = new BangListener();
public GoBangUI1(){
setTitle("五子棋");
setSize(1000,1000);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLayout(new FlowLayout());
setVisible(true);
for (int i = 0; i < s.length; i++) {
JButton jb = new JButton(s[i]);
jb.addActionListener(bl);
add(jb);
}
bl.g = getGraphics();
addMouseListener(bl);
bl.jf = this;
}
@Override
public void paint(Graphics g) {
super.paint(g);
//表格重绘
bl.paintUI();
bl.paintChess();
}
}
通过继承界面类的方式来创建,并且在构造方法中进行初始化。创建我们所需要的按钮开始以及悔棋。同时重写paint()方法,保证刷新界面后可以复现。这次将我们所需要的方法创建在一个类中进行统一调用与处理。
2.监听
创建一个类实现监听,并且将我们的方法包继承他,在创建一个类,继续继承方法包在其中实现我们所需要的监听,以及可以调用方法包里的方法。
public class Listener implements MouseListener, ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
}
@Override
public void mouseClicked(MouseEvent e) {
}
@Override
public void mousePressed(MouseEvent e) {
}
@Override
public void mouseReleased(MouseEvent e) {
}
@Override
public void mouseEntered(MouseEvent e) {
}
@Override
public void mouseExited(MouseEvent e) {
}
}
public class MethodPackage extends Listener {
public class BangListener extends MethodPackage{
@Override
public void mousePressed(MouseEvent e) {
int x = e.getX();
int y = e.getY();
playGame(x,y);
}
@Override
public void actionPerformed(ActionEvent e) {
String ac = e.getActionCommand();
if(ac.equals("开始")){
chessFlag = 1;
JButton jb = (JButton) e.getSource();
jb.setText("重新开始");
} else if (ac.equals("悔棋")) {
backOne();
}else if (ac.equals("重新开始")){
remake();
JButton jb = (JButton) e.getSource();
jb.setText("重新开始");
}
}
}
在我们这个监听中只需要实现我们所需要的监听,点击以及按钮的监听。别忘了在界面中创建监听对象,同时将按钮监听功能添加进去。按下鼠标进行下棋,只需要获取此时的坐标,传入下棋的方法中。在按钮监听中获取按钮的文本分别调用各自方法。
3.各方法的实现
package lcr0413.GoBangV2;
import javax.swing.*;
import java.awt.*;
public class MethodPackage extends Listener {
final int X = 100, Y = 125, SIZE = 50, COL = 15;
Graphics g;//画笔
int chessFlag = 0;//判断颜色,以及是否开始
int[][] arr = new int[COL + 1][COL + 1];//存储旗子
Chess[] chessArr = new Chess[256];//存储旗子的坐标,及其旗子颜色
int index = 0;//下标
JFrame jf;
//表格重绘
public void paintUI() {
g.setColor(Color.orange);
g.fillRect(X - SIZE / 2, Y - SIZE / 2, 800, 800);
g.setColor(Color.BLACK);
for (int i = 0; i <= COL; i++) {
g.drawLine(X, Y + i * SIZE, X + COL * SIZE, Y + i * SIZE);//横
g.drawLine(X + i * SIZE, Y, X + i * SIZE, Y + COL * SIZE);//列
}
}
//棋子重绘
public void paintChess() {
if (arr == null) {
return;
}
//遍历数组
for (int i = 0; i < arr.length; i++) {
for (int j = 0; j < arr[i].length; j++) {
if (arr[i][j] != 0) {
drawChess(i, j, arr[i][j]);
}
}
}
}
//悔棋
public void backOne() {
if (index - 1 < 0) {
/*System.out.println("无法悔棋");*/
new UI("无法悔棋");
return;
}
chessFlag = arr[chessArr[index - 1].r][chessArr[index - 1].c];
arr[chessArr[index - 1].r][chessArr[index - 1].c] = 0;
chessArr[index] = null;
index--;
jf.repaint();
}
//下棋
public void playGame(int x, int y) {
int c = (x - X + SIZE / 2) / SIZE;
int r = (y - Y + SIZE / 2) / SIZE;
//检查是否可以下入
if (!chessPlay(r, c, x, y)) {
return;
}
arr[r][c] = chessFlag;//存入棋子颜色
chessArr[index] = new Chess(r, c, chessFlag);
index++;
//改变棋子颜色
if (chessFlag == 1) {
g.setColor(Color.BLACK);
drawChess(r, c, chessFlag);
chessFlag = 2;
} else if (chessFlag == 2) {
g.setColor(Color.WHITE);
drawChess(r, c, chessFlag);
chessFlag = 1;
}
if (new Win().isWin(arr, r, c)) {
System.out.println("恭喜你获胜");
if (chessArr[index - 1].chessFlag == 1) {
new UI("恭喜黑子获胜");
} else {
new UI("恭喜白子获胜");
}
chessFlag = 0;
}
}
//下棋检测是否可以
public boolean chessPlay(int r, int c, int x, int y) {
if (chessFlag == 0) {
System.out.println("请开始游戏");
new UI("请开始游戏");
return false;
}
if (c > COL || r > COL || x < X - SIZE / 2 || y < Y - SIZE / 2) {
System.out.println("请在范围内下棋");
return false;
}
if (arr[r][c] != 0) {
System.out.println("禁止重复下棋");
return false;
}
return true;
}
//画棋子
public void drawChess(int r, int c, int chessFlag) {
if (chessFlag == 1) {
g.setColor(Color.BLACK);
} else if (chessFlag == 2) {
g.setColor(Color.WHITE);
}
int x = X + c * SIZE - SIZE / 2;
int y = Y + r * SIZE - SIZE / 2;
g.fillOval(x, y, SIZE, SIZE);
}
//重新开始
public void remake() {
for (int i = 0; i < arr.length; i++) {
for (int j = 0; j < arr[i].length; j++) {
arr[i][j] = 0;
}
}
for (int i = 0; i < chessArr.length; i++) {
chessArr[i] = null;
}
index = 0;
jf.repaint();
chessFlag = 1;
}
}
在这先定义常数,下棋网格所在的左上角坐标XY以及每行的间隔SIZE,以及行数COL
需要画笔g,以便在界面中获取界面的画笔。定义一个Flag用于判断颜色,初始为0,黑棋为1,白棋为2.定义一个二维数组存放旗子,即放入0,1,2代表棋子。并且定义一个一位数组里面写旗子在二维数组中的坐标以及棋子颜色。还有它的下标。创建界面jf
package lcr0413.GoBangV2;
public class Chess {
int r,c,chessFlag;
public Chess(int r, int c,int chessFlag) {
this.r = r;
this.c = c;
this.chessFlag = chessFlag;
}
}
3.1表格重绘
可以使用上面的方法,直接绘制。但刷新时会出现长时间闪过。因此也可以用缓存图片的方法。
public void paintUI() {
/* g.setColor(Color.orange);
g.fillRect(X - SIZE / 2, Y - SIZE / 2, 800, 800);
g.setColor(Color.BLACK);
for (int i = 0; i <= COL; i++) {
g.drawLine(X, Y + i * SIZE, X + COL * SIZE, Y + i * SIZE);//横
g.drawLine(X + i * SIZE, Y, X + i * SIZE, Y + COL * SIZE);//列
}*/
BufferedImage ima = new BufferedImage((COL + 1) * SIZE, (COL + 1) * SIZE, 2);
Graphics g1 = ima.getGraphics();
g1.setColor(Color.ORANGE);
g1.fillRect(0, 0, (COL + 1) * SIZE, (COL + 1) * SIZE);
g1.setColor(Color.BLACK);
for (int i = 0; i <= COL; i++) {
g1.drawLine(SIZE / 2, SIZE / 2 + i * SIZE, SIZE / 2 + COL * SIZE, SIZE / 2 + i * SIZE);
g1.drawLine(SIZE / 2 + i * SIZE, SIZE / 2, SIZE / 2 + i * SIZE, SIZE / 2 + COL * SIZE);
}
g.drawImage(ima, X - SIZE / 2, Y - SIZE / 2, null);
}
先创建ima接着获取它的画笔,将网格画在缓存图片上,最后画出image即可。
3.2下棋
我们将左上角第一个坐标记为0,0那么算出网格各点对应坐标使用c,r来表示。接着判断是否可以存入,(方法将在下面提及)。可以的话在一位数组中存入坐标r,c以及flag,接着实现改变棋子颜色的功能,flag为1则为黑色画完点后赋值2。画完棋子后进行是否获胜的判断。
3.2检测是否可以下棋
有三种情况不能下棋。
第一种就是未开始游戏,这里用flag来判断,当点击开始游戏后flag会被赋值为1。
第二种是未在范围内下棋,那么由算出的c,r以及原本的x,y来进行判断即可。
第三种是重复下棋,此时我们在二维数组中进行查找如果这个点的值已经不为0那么就有棋子了,不让直接返回即可。
3.3画棋子
将上面传进来的r,c进行复原,算出应该下在哪个位置,在对应点下入即可。同时也可实现改变颜色的功能。
3.4重新开始
在我们某方获得胜利之后进行将元素全部初始化,以及重新绘制棋盘。
3.5棋子的重绘
首先对二维数组进行判断是否为空,为空则没有棋子直接返回。有则遍历二维数组如果该点不为空那么画出此点。
3.6悔棋
先对一维数组的下标进行判断,减一后小于0代表没有棋子,那么直接返回即可。有则从二维数组中找出最后一个下的棋子,坐标由一维数组进行查找。找到后将二维数组最后一个下的棋子初始化,删去一位数组中记录它的坐标,下标减一后进行重绘。
4.判断输赢
package lcr0413.GoBangV2;
public class Win {
public boolean isWin(int[][] arr, int r, int c) {
if (findRow(arr, r, c) >= 5 || findCol(arr, r, c) >= 5 || findLI(arr, r, c) >= 5 || findRI(arr, r, c) >= 5) {
return true;
}
return false;
}
//横向
public int findRow(int[][] arr, int r, int c) {
int count = 1;
for (int i = c + 1; i < arr[r].length; i++) {
if (arr[r][i] == arr[r][c]) {
count++;
}
}
for (int i = c - 1; i >= 0; i--) {
if (arr[r][i] == arr[r][c]) {
count++;
}
}
return count;
}
//纵向
public int findCol(int[][] arr, int r, int c) {
int count = 1;
for (int i = r + 1; i < arr.length; i++) {
if (arr[i][c] == arr[r][c]) {
count++;
}
}
for (int i = r - 1; i >= 0; i--) {
if (arr[i][c] == arr[r][c]) {
count++;
}
}
return count;
}
//右斜
public int findRI(int[][] arr, int r, int c) {
int count = 1;
for (int i = r + 1, j = c + 1; i < arr.length && j < arr[r].length; i++, j++) {
if (arr[i][j] == arr[r][c]) {
count++;
}
}
for (int i = r - 1, j = c - 1; i >= 0 && j >= 0; i--, j--) {
if (arr[i][j] == arr[r][c]) {
count++;
}
}
return count;
}
//左斜
public int findLI(int[][] arr, int r, int c) {
int count = 1;
for (int i = r - 1, j = c + 1; i >= 0 && j < arr[r].length; i--, j++) {
if (arr[i][j] == arr[r][c]) {
count++;
}
}
for (int i = r + 1, j = c - 1; i < arr.length && j >= 0; i++, j--) {
if (arr[i][j] == arr[r][c]) {
count++;
}
}
return count;
}
}
判断输赢我们需要下入棋子的二维数组,以及最后一个棋子的坐标。
接着对他进行横向纵向斜着的四个方向的判断,如果存在五个棋子的情况则获胜。
以横向判断举例:定义一个数为1,代表棋子颜色相同的个数,接着对棋子左边以及右边分别进行判断。如果下一个棋子与远棋子相同则count加一接着判断下一个,如果不相同则中断,判断另一个方向。
其它放心同理。
在使用这些方法后即可完成简易的人人对战五子棋。
实现:
悔棋: