简单五子棋制作

基本界面


一  主框架的创建及棋盘的绘制
①主框架函数的创建

public class ChessUI extends JFrame {

	public static void main(String[] args) {
		ChessUI ui= new ChessUI();
		ui.initChessUI();
	}
	
	public void initChessUI(){
		this.setSize(600,600);
		this.setTitle("五子棋");
		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		
		JPanel panel = new MyPanel();
		//设置面板颜色
		panel.setBackground(new Color(200,200,100));
		//为面板添加鼠标监听器
		ChessListener lis = new ChessListener(panel);
		panel.addMouseListener(lis);
		
		this.add(panel);
		this.setVisible(true);
	}
}


②因为五子棋制作时一些数据比不可少  且每个类都要用到 所以定义一个接口 来存放数据
public interface Config {
	int X0 = 40, Y0 = 40;// 棋盘左上角的起始点的坐标
	int ROWS = 15, COLS = 15;// 棋盘的行和列
	int SIZE = 36;// 单元格大小
	int CHESS_SIZE = 30;// 棋子大小
	
	
	//定义一个数组用来保存棋盘的状态
	byte[][] CHESSES = new byte[ROWS][COLS];
}


注  byte  CHESSES数组 用来存放棋盘每个位置的状态 没有棋子时时0 ,黑棋子时是1,白棋子时是-1,数组中每个元素都对应棋盘上相应的位置


③panel面板上 用来绘制棋盘 棋子    所以必须重写panit方法  且要加上MouseListener监听器

定义一个MyPanel类 来继承 Panel
public class MyPanel extends JPanel implements Config{

	@Override
	public void paint(Graphics g) {
		// TODO Auto-generated method stub
		super.paint(g);
			}

}


④在Mypanel类中 paint函数里 画棋盘
for (int i = 0; i < ROWS; i++) {
			g.drawLine(X0, Y0 + i * SIZE, X0 + (COLS-1) * SIZE, Y0 + i * SIZE);
		}
		for (int i = 0; i < COLS; i++) {
			g.drawLine(X0 + SIZE * i, Y0, X0 + SIZE * i, Y0 + SIZE* (ROWS - 1));
		}



二 鼠标监听器的添加
为panel面板添加鼠标监听器  当鼠标点击棋盘时  在棋盘上绘制 棋子


public class ChessListener implements MouseListener, Config {

	public JPanel MyPanel;
	public Graphics g;
	private byte flag = 1; // 记录棋子颜色

	private boolean isdraw = true;   //判断是否继续绘制

	public ChessListener(JPanel MyPanel) {
		this.MyPanel = MyPanel;
	}

	@Override
	public void mousePressed(MouseEvent e) {
		// TODO Auto-generated method stub
		
	}

	
	
	@Override
	public void mouseReleased(MouseEvent e) {
		// TODO Auto-generated method stub

	}

	@Override
	public void mouseEntered(MouseEvent e) {
		// TODO Auto-generated method stub

	}

	@Override
	public void mouseExited(MouseEvent e) {
		// TODO Auto-generated method stub

	}

	@Override
	public void mouseClicked(MouseEvent e) {
		// TODO Auto-generated method stub

	}

}

注 通过构造函数来获得panel面板 来获得Graphics 来绘制

②在ChessListener中添加函数 用来绘制棋子
public void putChess(int r, int c, byte flag) {
		// 用数组记录绘制的位置 颜色
		CHESSES[r][c] = flag;

		// 当flag==1时 绘制黑子
		if (flag == 1) {
			g.setColor(Color.black);
			flag = -1;
		} else {
			g.setColor(Color.white);
			flag = 1;
		}
		// 以该交叉点坐标为圆心放棋子
		g.fillOval(c * SIZE + X0 - CHESS_SIZE / 2, r * SIZE + Y0 - CHESS_SIZE
				/ 2, CHESS_SIZE, CHESS_SIZE);
		
	}


注:通过参数来传递要绘制棋子的位置 类型(颜色)再以该位置为圆心画圆

③在监听器中添加判断输赢的方法
  通过扫描最后一次放子的位置 横向扫描 纵向扫描 左斜扫描 右斜扫描
来判断是否有5个相同颜色的棋子在同一条直线上
具体代码如下
// 判断获胜函数
	public void Judge(int xx, int yy) {
		int fg = CHESSES[xx][yy];
		String winner = null;
		if (fg == 1)
			winner = "黑子";
		else if (fg == -1)
			winner = "白子";
		// 设置计数器
		int count = 0;

		// ********************扫描纵向棋子************************

		// 扫描当前棋子下面4个棋子
		for (int i = 1; i <= 4; i++) {
			if (xx + i >= 0 && xx + i < ROWS) {
				if (CHESSES[xx + i][yy] == fg) {
					count++; // 如果是相同棋子 count加1
				} else
					break; // 否则停止扫描
			}
		}
		// 扫描当前棋子上面4个棋子
		for (int i = 1; i <= 4; i++) {
			if (xx - i >= 0 && xx - i < ROWS) {
				if (CHESSES[xx - i][yy] == fg) {
					count++;
				} else
					break;
			}
		}
		if (count == 4) {
			isdraw = false;
			JOptionPane.showMessageDialog(null, "1恭喜" + winner + "获胜");
			return;
		}

		// ********************扫描横向棋子************************
		count = 0;
		for (int i = 1; i <= 4; i++) {
			if (yy + i >= 0 && yy + i < COLS) {
				if (CHESSES[xx][yy + i] == fg) {
					count++; // 如果是相同棋子 count加1
				} else
					// 否则停止扫描
					break;
			}
		}
		for (int i = 1; i <= 4; i++) {
			if (yy - i >= 0 && yy - i < COLS) {
				if (CHESSES[xx][yy - i] == fg) {
					count++;
				} else
					break;
			}
		}
		if (count == 4) {
			isdraw = false;
			JOptionPane.showMessageDialog(null, "2恭喜" + winner + "获胜");
			return;
		}

		// ********************扫描右斜向棋子************************
		count = 0;
		// 扫描斜下
		for (int i = 1; i <= 4; i++) {
			if (xx + i >= 0 && xx + i < ROWS && yy + i >= 0 && yy + i < COLS) {
				if (CHESSES[xx + i][yy + i] == fg) {
					count++;
				} else
					break;
			}
		}

		// 扫描斜上
		for (int i = 1; i <= 4; i++) {
			if (xx - i >= 0 && xx - i < ROWS && yy - i >= 0 && yy - i < COLS) {
				if (CHESSES[xx - i][yy - i] == fg) {
					count++;
				} else
					break;
			}
		}
		if (count == 4) {
			isdraw = false;
			JOptionPane.showMessageDialog(null, "3恭喜" + winner + "获胜");
			return;
		}

		// ********************扫描左斜向棋子************************
		count = 0;
		// 扫描斜下
		for (int i = 1; i <= 4; i++) {
			if (xx + i >= 0 && xx + i < ROWS && yy - i >= 0 && yy - i < COLS) {
				if (CHESSES[xx + i][yy - i] == fg) {
					count++;
				} else
					break;
			}
		}

		// 扫描斜上
		for (int i = 1; i <= 4; i++) {
			if (xx - i >= 0 && xx - i < ROWS && yy + i >= 0 && yy + i < COLS) {
				if (CHESSES[xx - i][yy + i] == fg) {
					count++;
				} else
					break;
			}
		}

		if (count == 4) {
			isdraw = false;
			JOptionPane.showMessageDialog(null, "4恭喜" + winner + "获胜");
			return;
		}

	}


注:①参数是刚放下棋子的位置 flag为棋子类型(颜色)
    ②每次扫描右分为两个阶段  


④实现监听器中mousePressed函数 当鼠标点击下后 要在棋盘上绘制棋子
public void mousePressed(MouseEvent e) {
		// 获得鼠标的位置
		int x = e.getX();
		int y = e.getY();

		// 获得和该位置最近的交叉点的坐标
		int r = (y - Y0) / SIZE;
		int c = (x - X0) / SIZE;

		if ((y - Y0) % SIZE > SIZE / 2) {
			r++;
		}
		if ((x - X0) % SIZE > SIZE / 2) {
			c++;
		}

		if (!isOver && r >= 0 && r < ROWS && c >= 0 && c < COLS
				&& CHESSES[r][c] == 0) {
			// 计算交叉点坐标
			int x1 = X0 + SIZE * c;
			int y1 = Y0 + SIZE * r;

			// 将棋子状态保存起来
			CHESSES[r][c] = flag;

			if (flag == 1) {
				g.setColor(Color.BLACK);
				flag = -1;
			} else {
				g.setColor(Color.WHITE);
				flag = 1;
			}
            putChess(r, c, flag);
                    }

				
     }


注:①要在putChess函数中 添加判断输赢函数Judge
    ②通过一系列运算 将点击时的坐标 转换为要绘制在棋盘上的坐标 再通过putChess函数来绘制棋子


以上只基本实现了人人对战
我自己做了一个简易版的人机对战 

三 添加机器算法

①添加机器人 Robot类  用来计算出机器要下棋子的坐标
public class Robot implements Config {

	Point p;// 机器放棋子的位置

	// 如何根据棋盘的局势计算放子位置
	public Point checkPoint() {
      }
}


②要实现checkPoint函数 我又创建了一个类   CheckChess类
该类中有四个函数  分别扫描指定位置4个方向  并返回相同颜色棋子个数
具体代码如下:
public class CheckChess implements Config {

	public int flag = -1;

	public CheckChess(int flag) {
		this.flag = flag;
	}
	
	public void Setflag(int flag){
		this.flag = flag;
	}

	// 判断当前位置横向相同的棋子个数
	public int HengCheck(int r, int c) {
		int count = 0;
		for (int i = c + 1; i < COLS; i++) {
			if (CHESSES[r][i] == flag) {
				count++; // 如果是相同棋子 count加1
			} else
				// 否则停止扫描
				break;
		}
		for (int i = c - 1; i >= 0; i--) {
			if (CHESSES[r][i] == flag) {
				count++;
			} else
				break;
		}
		return count;
	}

	// 判断当前位置竖向的棋子个数
	public int ShuCheck(int r, int c) {
		// 扫描当前棋子下面棋子
		int count = 0;
		for (int i = r + 1; i < ROWS; i++) {

			if (CHESSES[i][c] == flag) {
				count++; // 如果是相同棋子 count加1
			} else
				break; // 否则停止扫描

		}
		// 扫描当前棋子上面棋子
		for (int i = r - 1; i >= 0; i--) {
			if (CHESSES[i][c] == flag) {
				count++;
			} else
				break;
		}
		return count;
	}

	// 判断当前位置左斜的棋子个数
	public int LCheck45(int r, int c) {
		int count = 0;
		for (int i = r - 1, j = c + 1; i >= 0 && j < COLS; i--, j++) {
			if (CHESSES[i][j] == flag) {
				count++;
			} else {
				break;
			}

		}

		for (int i = r + 1, j = c - 1; i < ROWS && j >= 0; i++, j--) {
			if (CHESSES[i][j] == flag) {
				count++;
			} else {
				break;
			}

		}

		return count;

	}

	// 判断当前位置左斜的棋子个数
	public int RCheck45(int r, int c) {
		int count = 0;
		for (int i = r - 1, j = c - 1; i >= 0 && j >=0; i--, j--) {
			if (CHESSES[i][j] == flag) {
				count++;
			} else {
				break;
			}

		}

		for (int i = r + 1, j = c + 1; i < ROWS && j <COLS; i++, j++) {
			if (CHESSES[i][j] == flag) {
				count++;
			} else {
				break;
			}

		}

		return count;

	}

}


注; flag为要扫描棋子的颜色

③实现Robot类中 checkPoint函数
// 如何根据棋盘的局势计算放子位置
	public Point checkPoint() {

		CheckChess check = new CheckChess(-1);
		// 扫描棋盘上没放子的地方 周围有4个一样的棋子
		for (int r = 0; r < ROWS; r++) {
			for (int c = 0; c < COLS; c++) {
				if (CHESSES[r][c] == 0) {
					// 寻找横纵斜4个方向是否有4个相同的白子
					check.Setflag(-1);
					if (check.HengCheck(r, c) == 4 || check.ShuCheck(r, c) == 4
							|| check.RCheck45(r, c) == 4
							|| check.LCheck45(r, c) == 4)
						return new Point(r, c);
					check.Setflag(1);
					if (check.HengCheck(r, c) == 4 || check.ShuCheck(r, c) == 4
							|| check.RCheck45(r, c) == 4
							|| check.LCheck45(r, c) == 4)
						return new Point(r, c);

				}
			}
		}

		// 扫描棋盘上没放子的地方 周围有3个一样的棋子
		check.Setflag(-1);
		for (int r = 0; r < ROWS; r++) {
			for (int c = 0; c < COLS; c++) {
				if (CHESSES[r][c] == 0) {
					// 寻找横纵斜4个方向是否有3个相同的白子
					check.Setflag(-1);
					if (check.HengCheck(r, c) == 3 || check.ShuCheck(r, c) == 3
							|| check.RCheck45(r, c) == 3
							|| check.LCheck45(r, c) == 3)
						return new Point(r, c);

					check.Setflag(1);
					if (check.HengCheck(r, c) == 3 || check.ShuCheck(r, c) == 3
							|| check.RCheck45(r, c) == 3
							|| check.LCheck45(r, c) == 3)
						return new Point(r, c);

				}
			}
		}

		// 扫描棋盘上没放子的地方 周围有2个一样的棋子
		
		for (int r = 0; r < ROWS; r++) {
			for (int c = 0; c < COLS; c++) {
				if (CHESSES[r][c] == 0) {
					// 寻找横纵斜4个方向是否有2个相同的白子
					check.Setflag(-1);
					if (check.HengCheck(r, c) == 2 || check.ShuCheck(r, c) == 2
							|| check.RCheck45(r, c) == 2
							|| check.LCheck45(r, c) == 2)
						return new Point(r, c);
				}
			}
		}

		// 扫描棋盘上没放子的地方 周围有1个一样的棋子
		for (int r = 0; r < ROWS; r++) {
			for (int c = 0; c < COLS; c++) {
				if (CHESSES[r][c] == 0) {
					// 寻找横纵斜4个方向是否有1个相同的白子
					check.Setflag(-1);
					if (check.HengCheck(r, c) == 1 || check.ShuCheck(r, c) == 1
							|| check.RCheck45(r, c) == 1
							|| check.LCheck45(r, c) == 1)
						return new Point(r, c);
				}
			}
		}
		return new Point(7, 7);

	}
    


注:① 函数返回的时机器要下棋子在棋盘上的位置
    ② 该函数只是最简易的算法  先逐个扫描棋盘  当扫描到没有放棋子的位置时 判断这个位置4个方向有没有4相同白色的棋子 如果有则在返回该位置 如果没有就判断该位置有没有4个相同的黑色棋子 如果有则返回该位置 没有就继续扫描。当扫描完棋盘后还是没有返回位置,就再重新扫描棋盘 这次判断的是 是否具有3个相同的棋子  以此类推
    ③最后返回的是白子的起始位置



④修改监听器中mousePressed函数
public void mousePressed(MouseEvent e) {
		// TODO Auto-generated method stub
		g = MyPanel.getGraphics();

		int x, y; // 按下鼠标时坐标
		x = e.getX();
		y = e.getY();
		int r = (y - Y0) / SIZE; // 行数
		int c = (x - X0) / SIZE; // 列数

		if ((x - X0) % SIZE > SIZE / 2) {
			c++;
		}
		if ((y - Y0) % SIZE > SIZE / 2) {
			r++;
		}

		if (r >= 0 && r < ROWS && c >= 0 && c < COLS && CHESSES[r][c] == 0
				&& isdraw) {
            
//		    绘制玩家下的棋子
			putChess(r, c, (byte) 1);
//		  产生机器要下的棋子坐标  并绘制棋子
			Point p = robot.checkPoint();
			putChess(p.x, p.y, (byte) -1);
			

		}

	}


四 重写MyPanel中panit方法
public void paint(Graphics g) {
		// TODO Auto-generated method stub
		super.paint(g);
		for (int i = 0; i < ROWS; i++) {
			g.drawLine(X0, Y0 + i * SIZE, X0 + (COLS-1) * SIZE, Y0 + i * SIZE);
		}
		for (int i = 0; i < COLS; i++) {
			g.drawLine(X0 + SIZE * i, Y0, X0 + SIZE * i, Y0 + SIZE* (ROWS - 1));
		}
		
		for(int i=0;i<ROWS;i++){
			for(int j=0;j<ROWS;j++){
				if(CHESSES[i][j]!=0){
					switch ((int)CHESSES[i][j]){
					case 1:
						g.setColor(Color.black);
						break;
					case -1:
						g.setColor(Color.white);
						break;
					}
					g.fillOval(j * SIZE + X0 - CHESS_SIZE / 2, i * SIZE + Y0- CHESS_SIZE / 2, CHESS_SIZE, CHESS_SIZE);
				}
			}
		}
		
	}




上面过程基本实现了五子棋方法  添加了简单的机器人  但还存在许多缺陷  比如机器算法没有算出放棋子的最优位置 机器算法起始位置都是一样 的 等 这些bug还有待我去完善

猜你喜欢

转载自592713711.iteye.com/blog/2157742