java五子棋

来源:互联网 发布:sql union和join 编辑:程序博客网 时间:2024/05/16 01:04
java五子棋总结
刚刚学习java不久,尝试自己做了一个五子棋先附上一个运行效果的图:



五子棋的主要目的:
1.练习java面向对象的编程思想
2.掌握监听器的使用(鼠标监听器)

制作五子棋为了代码的可扩充性,我们首先要定义一个五子棋的各个属性的接口:
/** * 五子棋的相关属性 * @author Administrator * */public interface Config {public static final int X0 = 45;//棋盘的左上角的横坐标public static final int Y0 = 45;//棋盘的左上角的纵坐标public static final int ROWS = 15;//横向的线条数public static final int COUMNS = 15;//纵向的线条数public static final int CHESS_SIZE = 30;//棋子的直径public static final int SIZE = 40;//单元格的大小}

接下来就是完成五子棋的主界面:
主界面完成的主要工作就是五子棋棋盘的绘制、棋子的重绘、以及添加相应的按钮:
import javax.swing.JPanel;import javax.swing.JButton;import javax.swing.JFrame;import java.awt.Color;import java.awt.Dimension;import java.awt.FlowLayout;import java.awt.Graphics;import javax.swing.ImageIcon;public class GameUI extends JPanel implements Config {//记录棋子的位置的数组private int[][] array;//设置一个开始的flagboolean flag =false;public void inIt(){//创建一个窗体,并给其设置属性JFrame jf = new JFrame("五子棋");jf.setSize(800,700);jf.setLocationRelativeTo(null);jf.setDefaultCloseOperation(3);jf.setResizable(false);jf.setLayout(new FlowLayout(0,0,0));//设置JPanel组件的大小和背景颜色this.setPreferredSize(new Dimension(650,700));this.setBackground(new Color(204,162,129));jf.add(this);//再设置一个JPanelJPanel jp = new JPanel ();jp.setPreferredSize(new Dimension(140,700));jp.setBackground(Color.WHITE);//添加一个开始的按钮JButton startB = new JButton("开始");createButton(jp,startB,20,150);//添加一个悔棋的按钮JButton huiqiB = new JButton("悔棋");createButton(jp,huiqiB,20,250); //添加一个重新开始的按钮JButton restartB = new JButton("重新开始");createButton(jp,restartB,20,200);//添加一个结束游戏按钮JButton endgameB = new JButton("结束游戏");createButton(jp,endgameB,20,300);//添加人机对战按钮JButton computerPk = new JButton("人机对战");createButton(jp,computerPk,20,350);jf.add(jp);//设置窗口可见jf.setVisible(true);Graphics g = this.getGraphics();//创建一个监听器GameListener gL = new GameListener(g,this,flag);this.addMouseListener(gL);//给按钮添加动作监听器startB.addActionListener(gL);huiqiB.addActionListener(gL);restartB.addActionListener(gL);endgameB.addActionListener(gL);computerPk.addActionListener(gL);//获取存储数组array = gL.getArray();}/** * 重写重绘函数 */public void paint(Graphics g){super.paint(g);//画出五子棋的棋盘drawChessTable(g);//重绘五子棋的棋子drawChess(g);}/** * 画出棋盘 * @param g画笔 */public void drawChessTable(Graphics g){//设置五子棋的棋盘的属性ImageIcon chessBoardImageIcon = new ImageIcon(this.getClass().getResource("img\\chessboard.jpg"));g.drawImage(chessBoardImageIcon.getImage(), 0, 0, this.getWidth(), this.getHeight()-30, null);for(int i =0;i<Config.ROWS;i++){g.drawLine(Config.X0, Config.Y0+Config.SIZE*i,Config.X0+(Config.ROWS-1)*Config.SIZE,Config.Y0+Config.SIZE*i);g.drawLine(Config.X0+Config.SIZE*i, Config.Y0,Config.X0+Config.SIZE*i, Config.Y0+(Config.ROWS-1)*Config.SIZE);}}/** * 重新画出棋子的位置 * @param g画笔 */public void drawChess(Graphics g){//创建一个画图片的对象ImageIcon chessImageIcon = null;//使用遍历数组存储棋子的位置for(int i=0;i<array.length;i++){for(int j=0;j<array[i].length;j++){if(array[i][j] == 1){chessImageIcon = new ImageIcon(this.getClass().getResource("img\\heiqi.png"));g.drawImage(chessImageIcon.getImage(),Config.X0 + j * Config.SIZE - Config.CHESS_SIZE/2,Config.Y0  + i * Config.SIZE - Config.CHESS_SIZE/2,Config.CHESS_SIZE,Config.CHESS_SIZE, null);}else if(array[i][j] == 2){chessImageIcon = new ImageIcon(this.getClass().getResource("img\\baiqi.png"));g.drawImage(chessImageIcon.getImage(),Config.X0 + j * Config.SIZE - Config.CHESS_SIZE/2,Config.Y0  + i * Config.SIZE - Config.CHESS_SIZE/2,Config.CHESS_SIZE,Config.CHESS_SIZE, null);}}  }}/** *  * @param jpa * @param jb * @param x 按钮的左上角的横坐标 * @param y按钮的坐上角的纵坐标 */public void createButton(JPanel jpa,JButton jb,int x,int y){jpa.setLayout(null);jb.setBounds(x, y,100, 30);jpa.add(jb);}public static void main (String[] args){//创建游戏界面类GameUI gameUi = new GameUI();//调用类的实现函数gameUi.inIt();}}

接下来就是添加相应的监听器:
1.鼠标监听器实现下棋
2.动作监听器实现对按钮的控制

import java.awt.event.ActionEvent;import java.awt.event.ActionListener;import java.awt.event.MouseAdapter;import java.awt.event.MouseEvent;import javax.swing.JButton;import javax.swing.JFrame;import javax.swing.JPanel;import java.awt.Color;import java.awt.Graphics;public class GameListener extends MouseAdapter implements ActionListener {// 设置一个画笔private Graphics g; // 记录棋子的数量的计数器privateint count = 0;// 棋子位于棋盘上的行和列数privateint r, c;//设置记录棋子横坐标和纵坐标的2个数组privateint[] setX = new int[Config.ROWS*Config.COUMNS+1];privateint[] setY = new int[Config.ROWS*Config.COUMNS+1];//创建一个JPane对象private JPanel jp;//创建一个标志private boolean flag;//创建一个字符串privateString s = "人人对战";private int x1=0,y1=0;// 设置一个二维数组存储棋子的位置privateint array[][] = new int[Config.ROWS][Config.COUMNS];//创建一个电脑AI对象private ComputerAI ca;//创建一个权值数组private int[][] weightArray ;public GameListener(Graphics g,JPanel jp,boolean flag) {this.g = g;this.jp = jp;this.flag = flag;}public void mouseClicked(MouseEvent e) {// 获取点击的时候x,y的坐标值int x = e.getX();int y = e.getY();if(flag){if(x>= Config.X0 && y >= Config.X0 && x<=Config.X0+(Config.ROWS-1)*Config.SIZE && y<=Config.Y0+(Config.ROWS-1)*Config.SIZE ){// 算出棋子的行数和列数r = (y - Config.Y0 + Config.CHESS_SIZE/2) /Config.SIZE;c = (x - Config.X0 + Config.CHESS_SIZE/2) /Config.SIZE;if(s.equals("人人对战")){playerPk(r,c);}else if(s.equals("人机对战")){pkComputer(r,c);}}}}/** * 添加动作事件 */public void actionPerformed(ActionEvent e){String str =e.getActionCommand();if(str.equals("开始")){flag = true;}else if(str.equals("悔棋")){if(count> 0){int x = setX[count];int y = setY[count];array[x][y] = 0;count--;jp.repaint();}}else if(str.equals("重新开始")){for(int i=0;i<array.length;i++){for(int j=0;j<array[i].length;j++){array[i][j] = 0;}}//计数器归0count = 0;jp.repaint();}else if(str.equals("结束游戏")){//将保存棋子的数组归0for(int i=0;i<array.length;i++){for(int j=0;j<array[i].length;j++){array[i][j] = 0;}}if(s.equals("人人对战")){}else if(s.equals("人机对战")){//将权值数组归0for(int i=0;i<weightArray.length;i++){for(int j=0;j<weightArray[i].length;j++){weightArray[i][j] = 0;}}}//计数器归0count = 0;jp.repaint();flag =false;}else if(str.equals("人机对战")){s = "人机对战";ca = new ComputerAI(array);weightArray = ca.getWeightArray();}}/** * 判断输赢得一方 * @param r当前棋子所在的行数 * @param c当前棋子所在的列数 */private void gameWin(int r,int c){ChessWin cw = new ChessWin(array);if(array[r][c] == 1){if(cw.Wingame(r, c)){winJF("黑棋获胜");flag = false;}}else if(array[r][c] == 2){if(cw.Wingame(r, c)){winJF("白棋获胜");flag = false;}}}/** * 赢棋的时候弹出的提示框 */private void winJF(String str){//创建一个新的提示框体JFrame jf = new JFrame();//设置其属性jf.setSize(300, 200);jf.setLocationRelativeTo(null);jf.setDefaultCloseOperation(3);//使用空布局jf.setLayout(null);//创建一个按钮,并设置其大小JButton jbu = new JButton(str);jbu.setBounds(80,50, 120,40);//添加到窗体里jf.add(jbu);jf.setVisible(true);WinnerListener wl = new WinnerListener(jf,jp,array,s,weightArray);jbu.addActionListener(wl);//游戏一方获胜计数器归0count = 0;}// 将array数组传递出去public int[][] getArray() {return array;}/** * 确定电脑下棋的下一步的行和列数 * @param a 人下棋的行数 * @param b人下的棋的列数 */private void computerNextStep(){int[] max= new int[weightArray.length];//存储行最大权值for(int i =0 ;i<weightArray.length;i++){//求出x方向上面的最大值int maxX = weightArray[i][0];for(int j =0;j<weightArray[i].length;j++){if(maxX < weightArray[i][j]){maxX = weightArray[i][j];}}max[i] = maxX;}int m =max[0];for(int i=1;i<max.length;i++){if(m<max[i]){m = max[i];}}int flag = 0;System.out.println("max="+m);for(int i =0 ;i<weightArray.length;i++){for(int j =0;j<weightArray[i].length;j++){if(weightArray[i][j] == m){//找出最大是的行和列标System.out.println("i ="+i+"   j="+j);x1 = i;y1 = j;System.out.println("x1="+x1+"    y1="+y1);flag = 1;break;}}if(flag == 1){break;}}}/** * 实现人机对战 * @param r 人下的棋子行数 * @param c人下棋子的劣势 */private void pkComputer(int r,int c){System.out.println("x1="+x1+"    y1="+y1);if( array[r][c] == 0 ){for(int i =0;i<30;i++){g.setColor(new Color(i*4,i*4,i*4));g.fillOval(Config.X0 + c * Config.SIZE - Config.CHESS_SIZE/2+i/2,Config.Y0  + r * Config.SIZE - Config.CHESS_SIZE/2+i/2,Config.CHESS_SIZE-i,Config.CHESS_SIZE-i);}array[r][c] = 1; // 1表示该位置为黑棋ca.ai();//更新权值数组computerNextStep(); //电脑下棋的下一步count++;//计数器加1//记录画出一个棋子后的x和y坐标setX[count] = r;setY[count] = c;}//判断黑棋是否获胜gameWin(r,c);if(array[x1][y1] == 0 ){for(int i =30,j=0;i>=0 && j<30;i--,j++){g.setColor(new Color(255-3*i,255-3*i,255-3*i));g.fillOval(Config.X0 + y1 * Config.SIZE - Config.CHESS_SIZE/2+j/2,Config.Y0  + x1 * Config.SIZE - Config.CHESS_SIZE/2+j/2,Config.CHESS_SIZE-j,Config.CHESS_SIZE-j);}array[x1][y1] = 2;//2表示该位置为白棋count++;//计数器加一setX[count] = x1;setY[count] = y1;}//判断白棋是否获胜gameWin(x1,y1);//将有棋子位置的权值归0updateWeight();}/** * 玩家之间的对战 * @param r 人下的棋子行数 * @param c人下棋子的劣势 */private void playerPk(int r,int c){// 存储棋子的位置if(array[r][c] == 0 ){if (count % 2 == 0) {for(int i =0;i<30;i++){g.setColor(new Color(i*4,i*4,i*4));g.fillOval(Config.X0 + c * Config.SIZE - Config.CHESS_SIZE/2+i/2,Config.Y0  + r * Config.SIZE - Config.CHESS_SIZE/2+i/2,Config.CHESS_SIZE-i,Config.CHESS_SIZE-i);}array[r][c] = 1; // 1表示该位置为黑棋} else if (count % 2 == 1) {for(int i =30,j=0;i>=0 && j<30;i--,j++){g.setColor(new Color(255-3*i,255-3*i,255-3*i));g.fillOval(Config.X0 + c * Config.SIZE - Config.CHESS_SIZE/2+j/2,Config.Y0  + r * Config.SIZE - Config.CHESS_SIZE/2+j/2,Config.CHESS_SIZE-j,Config.CHESS_SIZE-j);}array[r][c] = 2;//2表示该位置为白棋}}// 计数器加1count++;//判断赢得一方gameWin(r,c);//记录画出一个棋子后的x和y坐标setX[count] = r;setY[count] = c;}/** * 下棋之后将有棋子位置的权值改为0 */private void updateWeight(){for(int i =0;i<weightArray.length;i++){for(int j = 0;j<weightArray[i].length;j++){weightArray[i][j] = 0;}}}}
这里记录棋子的位置主要是使用一个二维数组,二维数组的行数和列数表示棋子所在的棋盘的行和列数。

接下来就是输赢得判定:
它的主要思想就是向八个方向扫描,看在某一个方向上面是否有相同颜色的棋子数大于5个的。
public class ChessWin {//定义一个存储棋子的数组int array[][];/** * 构造函数 * @param array存储棋子的数组 */public ChessWin(int[][] array) {this.array = array;}/** * 判断是否五子相连 * @param r表示棋子的所在的行数 * @param c表示棋子所在的列数 * @return如果五子相连返回true */public boolean Wingame(int r,int c){boolean flag = false;if(countX(r,c)>=5 || countY(r,c)>=5 || countXy(r,c)>=5 ||countYx(r,c)>=5){flag = true;}return flag;}/** * 判断水平方向上相连的棋子数 * @param r表示棋子的所在的行数 * @param c表示棋子所在的列数 * @return 水平方向上相连的棋子个数 */private int countX(int r,int c){int count =1;for(int i =c+1;i<array[r].length;i++){//向右检查if(array[r][i] == array[r][c]){count++;}else{break;}}for(int i = c-1;i>=0;i--){//向左检查if(array[r][i] == array[r][c]){count++;}else{break;}}return count;}/** * 判断竖直方向上相连的棋子数 * @param r表示棋子的所在的行数 * @param c表示棋子所在的列数 * @return 竖直方向上相连的棋子个数 */private int countY(int r,int c){int count = 1;//向上统计for(int i = r-1;i>=0;i--){if(array[i][c] == array[r][c]){count++;}else{break;}}//向下统计for(int i =r+1;i<array.length;i++){if(array[i][c] == array[r][c]){count++;}else{break;}}return count;}/** * 判断45度方向上相连的棋子数 * @param r表示棋子的所在的行数 * @param c表示棋子所在的列数 * @return 45度方向上相连的棋子个数 */private int countXy(int r,int c){int count =1;//向右上角统计for(int i = c+1,a = r-1;i<array.length && a>=0;i++,a--){if(array[a][i] == array[r][c]){count++;}else{break;}}//左下角统计for(int j = r+1, b = c-1;j<array.length && b>=0;j++,b--){if(array[j][b] == array[r][c]){count++;}else{break;}}return count;}/** * 判断135度方向上相连的棋子数 * @param r表示棋子的所在的行数 * @param c表示棋子所在的列数 * @return 相连的棋子数 */private int  countYx(int r,int c){int count=1;//左上角统计for(int i = c-1,a= r-1;i>=0 && a>=0;i--,a--){if(array[a][i] == array[r][c]){count++;}else{break;}}//右下角统计for(int j = c+1,b = r+1;j<array.length && b<array.length;j++,b++){if(array[b][j]  == array[r][c]){count++;}else{break;}}return count;}}

最后一步,就是实现人机对战。
这里用的到的算法思想就是权值算法,其主要思想就是:向8个方向上面扫描,统计八个相邻位置的权值,再将棋子下在权值最大的那个位置。
import java.util.HashMap;public class ComputerAI implements Config {//记录棋子的数组private int[][]  chessArray;//各个位置权值的数组private int[][] weightArray = new int[Config.ROWS][Config.ROWS];//权值表private HashMap<String,Integer> weightMap = new HashMap<String,Integer>();//记录棋子颜色变量private int chess = 0;private String str = "";public ComputerAI(int[][] chessArray) {super();this.chessArray = chessArray;}/** * AI算法的实现 */public void  ai(){//设置权值表putMap();//扫扫描整个数组for(int i=0;i<chessArray.length;i++){for(int j =0;j<chessArray[i].length;j++){if(chessArray[i][j] == 0){//向左扫描,并得到权值scanXLeft(i,j);getWeight(str,i,j);//向右扫描,并得到权值scanXRight(i,j);getWeight(str,i,j);//向上扫描并得到权值scanYUp(i,j);getWeight(str,i,j);//向下扫描并的到权值scanYDown(i,j);getWeight(str,i,j);//向右上角扫面并的到权值scanXyUp(i,j);getWeight(str,i,j);//左下角扫描并的到权值scanXyDown(i,j);getWeight(str,i,j);//向左上角扫描并的到权值scanYxUp(i,j);getWeight(str,i,j);//向右下角扫面scanYxDown(i,j);getWeight(str,i,j);}}}}/** * 设置哈希表的权值 */private void putMap(){//黑棋活四连weightMap.put("11110",new Integer(1000000));//黑棋死四连weightMap.put("11112",new Integer(1000000));//黑棋活三连weightMap.put("1110",new Integer(10000));//黑棋死三连weightMap.put("1112", new Integer(5000));//黑棋活两连weightMap.put("110", new Integer(400));//黑棋死两连weightMap.put("112", new Integer(20));//黑棋单子weightMap.put("10", new Integer(3));//白棋活四连weightMap.put("22220",new Integer(1000000));//白棋死四连weightMap.put("22221",new Integer(1000000));//白棋活三连weightMap.put("2220",new Integer(10000));//白棋死三连weightMap.put("2221", new Integer(5000));//白棋活两连weightMap.put("220", new Integer(400));//白棋死两连weightMap.put("221", new Integer(20));//白棋单子weightMap.put("20",new Integer(3));//扫描周围的时候,黑棋单子weightMap.put("1", new Integer(3));}/** * 向左扫描记录棋局 * @param i 记录棋子的数组的行标 * @param j 记录棋子的数组的列标 */private void scanXLeft(int i,int j){chess = 0;str = "";for(int k = j-1;k>=0;k--){//向左扫描if(chessArray[i][k]==0 && k== j-1){//旁边是空位break;}else if(chess == 0){//如果旁边是黑或者白子chess = chessArray[i][k];//记录颜色str +=  chessArray[i][k];//记录棋局}else if(chess ==  chessArray[i][k]){//如果旁边的棋子颜色一样str +=  chessArray[i][k];}else if( chess != chessArray[i][k]){//如果旁边的棋子的颜色不一样str +=  chessArray[i][k];break;}}}/** * 向右扫描记录棋局 * @param i 记录棋子的数组的行标 * @param j 记录棋子的数组的列标 */private void scanXRight(int i, int j){chess = 0;str = "";for(int a= j+1;a < chessArray.length;a++){//向右if(chessArray[i][a] == 0 && a== i+1){//旁边是空位break;}else if(chess == 0){//旁边为黑棋或者是白旗的时候chess = chessArray[i][a];//记录旁边棋子的颜色str += chessArray[i][a];//记录棋局}else if(chess == chessArray[i][a]){//旁边棋子的颜色相同str += chessArray[i][a];}else if(chess != chessArray[i][a]){//旁边的棋子颜色不同str += chessArray[i][a];break;}}}/** * 扫描竖直向上记录棋局 * @param i 记录棋子的数组的行标 * @param j  记录棋子的数组的列标 */private void scanYDown(int i, int j){chess = 0;//记录旁边棋子颜色str = "";for(int a= i-1;a>=0;a--){//向上扫描if(chessArray[a][j] == 0 && a== i-1){break;}else if(chess == 0){chess = chessArray[a][j];str += chessArray[a][j];}else if(chess == chessArray[a][j]){str += chessArray[a][j];}else if(chess != chessArray[a][j]){str += chessArray[a][j];break;}}}/** * 扫描竖直向下记录棋局 * @param i 记录棋子的数组的行标 * @param j 记录棋子的数组的列标 */private void scanYUp(int i,int j){chess = 0;//记录旁边棋子颜色str = "";for(int b = i+1;b<chessArray.length;b++){//向下扫描if(chessArray[b][j] == 0 && b== i+1){break;}else if(chess == 0){chess = chessArray[b][j];str += chessArray[b][j];}else if(chess == chessArray[b][j]){str += chessArray[b][j];}else if(chess != chessArray[b][j]){str += chessArray[b][j];break;}}}/** * 扫描45度向上记录棋局 * @param i 记录棋子的数组的行标 * @param j  记录棋子的数组的列标 */private void scanXyUp(int i,int j){chess = 0;//记录旁边棋子颜色str = "";for(int a=i-1,b=j+1;a>=0 && b<chessArray.length;a--,b++){//像右上角扫描if(chessArray[a][b] == 0 && a == i-1 && b == j+1){break;}else if(chess ==0){chess = chessArray[a][b];str += chessArray[a][b];}else if(chess == chessArray[a][b]){str += chessArray[a][b];}else if(chess != chessArray[a][b]){str += chessArray[a][b];break;}}}/** * 扫描45度向下方向记录棋局 * @param i 记录棋子的数组的行标 * @param j  记录棋子的数组的列标 */private void scanXyDown(int i, int j){chess = 0;//记录旁边棋子颜色str = "";for(int m=i+1,n=j-1;m< chessArray.length && n>=0;m++,n--){//向左下角扫描if(chessArray[m][n] == 0 && m == i+1 && n == j-1){break;}else if(chess ==0){chess = chessArray[m][n];str += chessArray[m][n];}else if(chess == chessArray[m][n]){str += chessArray[m][n];}else if(chess != chessArray[m][n]){str += chessArray[m][n];break;}}}/** * 扫描135度向上记录棋局 * @param i 记录棋子的数组的行标 * @param j 记录棋子的数组的列标 */private void scanYxUp(int i,int j){chess = 0;//记录旁边棋子颜色str = "";for(int a=i-1,b=j-1;a>=0 && b>=0;a--,b--){//像左上角上角扫描if(chessArray[a][b] == 0 && a == i-1 && b == j-1){break;}else if(chess == 0){chess = chessArray[a][b];str += chessArray[a][b];}else if(chess == chessArray[a][b]){str += chessArray[a][b];}else if(chess != chessArray[a][b]){str += chessArray[a][b];break;}}}/** * 扫描135度向下记录棋局 * @param i 记录棋子的数组的行标 * @param j 记录棋子的数组的列标 */private void scanYxDown(int i,int j){chess = 0;//记录旁边棋子颜色str = "";for(int m=i+1,n=j+1;m< chessArray.length && n< chessArray.length;m++,n++){//向左下角扫描if(chessArray[m][n] == 0 && m == i+1 && n == j+1){break;}else if(chess ==0){chess = chessArray[m][n];str += chessArray[m][n];}else if(chess == chessArray[m][n]){str += chessArray[m][n];}else if(chess != chessArray[m][n]){str += chessArray[m][n];break;}}}/** * 返回该位置的权值 * @param str 记录棋局的字符串 * @param i 记录棋子的数组的行标 * @param j 记录棋子的数组的列标 */private void getWeight(String str,int i,int j){//根据棋局字符串去权值表找权值Integer weight = weightMap.get(str);if(weight == null){weight = 0;}weightArray[i][j] += weight;}/** * 将权值数组传递出去 * @return 权值数组 */public int[][] getWeightArray() {return weightArray;}}
这样一个五子棋基本上算完成了,接下来就是通过调试得到自己想要的结果。

这里存在这样几个问题:
1.如何设计五子棋的主界面?
2.如何去除棋子周围的锯齿?





























2 0