Java学习笔记(六):五子棋玩家对战

一、实现功能

实现普通的双人五子棋,在用户界面可见棋盘状态(该谁下,模式选择),提醒用户操作,带无限悔棋功能。

二、算法思想

  1. 画出棋盘和棋子,实现重绘
  2. 鼠标监听器实现在棋盘的正确位置下棋子
  3. Check判断输赢

三、具体操作

  1. 自定义棋盘参数接口:Config
  2. JFrame中画棋盘,在鼠标监听器(MouseListener)中添加棋子,并实现重绘
  3. 用一个二维数chesses[][]组保存棋盘落子情况,黑子为1,白子为-1,空为0,每次重新开始时都要对整个棋盘进行初始化。
  4. 每次下完一个棋子之后判断是否有输赢,即判断当前下的这个棋子在chesses中是否有五连珠,如果有说明这个棋子胜利,弹窗提示。
  5. 判断方法check主要是对对棋子的上下左右,左上右下(斜向右),左下右上(斜向左)判断是否有5连珠,如果有返回true,下面的代码中用到static来定义方法,方便外部调用。
  6. 实现悔棋功能,每次下一个棋子就保存在二维数组back[][]中,也方便下一步的AI找坐标
  7. 给出各个操作的开关标记位,flag_start,flag_playr等,若按下悔棋键,直接删除上两次的棋子存储,调用重绘。

四、遇到问题

  1. 每次用java的时候都会面临的问题都是各种参数传来传去,画笔从这边给到另一边,同样的我这次不光是传递画笔,还要实现重绘,所以就直接把整个对象传过去,但是会出现问题。
  2. 棋盘绘制问题,这个没有什么好解释的,刚刚开始画总是对不齐,主要是循环问题,我是先循环画列再循环画行,都一样的。
  3. 棋子放置问题,一开始求棋子放置坐标的时候总会错,还有偏差,这次使用的是fillOval()这个方法,所以在算出坐标的时候还要减去棋子的一半作为这个方法的参数
  4. 判断输赢需要考虑八个方向,上下左右,左上右上,左下右下

五、附录代码

  • 画棋盘(简陋的界面)
package com.goband;

import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;

import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;

public class Checkerboard extends JPanel {
     
	private Repaint[] rep = new Repaint[1000];
	private int[][] chesses = new int[Config.rows][Config.columns];
	private JFrame jf = new JFrame();
	private JTextArea txt = new JTextArea(35, 20);

	/**
	 * 初始化五子棋窗体
	 */
	public void chshow() {
     
		jf.setSize(1000, 750);
		jf.setTitle("五子棋");
		jf.setDefaultCloseOperation(3);// 设置关闭方法
		jf.setLocationRelativeTo(null);// 设置居中
		// jf.getContentPane().setBackground(Color.white);//设置背景色
		// java设置背景色方法2,1设置getcontentPane不可见
		jf.setLayout(new BorderLayout());
		// JPanel center=new JPanel();

		this.setBackground(Color.ORANGE);
		jf.add(this, BorderLayout.CENTER);// 中间
		JPanel north = new JPanel();
		/*
		 * 东
		 */
		JPanel east = new JPanel();
		east.setPreferredSize(new Dimension(330, 0));
		jf.add(east, BorderLayout.EAST);
		JLabel jl = new JLabel(" = 游戏信息提示 = ");
		east.add(jl);
		/**
		 * 添加滚动条
		 */
		JScrollPane js = new JScrollPane();
		east.add(js);
		js.setBounds(10, 10, 30, 600);
		txt.setBounds(10, 10, 30, 600);
		js.setViewportView(txt);// 添加到滚动窗格
		// 下句会重复添加
		// east.add(txt);

		north.setPreferredSize(new Dimension(0, 50));
		jf.add(north, BorderLayout.NORTH);//

		DrawListener qizi = new DrawListener();
		String[] Botton = {
      "开始游戏", "悔棋", "重新开始", "人机对战", "玩家对战" };
		for (int i = 0; i < Botton.length; i++) {
     
			ImageIcon img = new ImageIcon(this.getClass().getResource(Botton[i] + ".png"));
			JButton jbu = new JButton(Botton[i], img);
			north.add(jbu);
			jbu.addActionListener(qizi);
		}
		// 设置边框布局
		jf.setVisible(true);
		Graphics g = this.getGraphics();
		qizi.setGraphics(g);
		qizi.setChesses(chesses);
		qizi.setrepaint(rep);
		qizi.setCheckerboard(this);
		this.addMouseListener(qizi);

	}

	public JTextArea getEast() {
     
		return txt;
	}

	public void initchesses() {
     
		for (int i = 0; i < Config.rows; i++) {
     
			for (int j = 0; j < Config.columns; j++) {
     
				chesses[i][j] = 0;
			}
		}
	}

	/**
	 * 重绘函数 绘制棋盘方法
	 */

	public void paint(Graphics gr) {
     
		super.paint(gr);
		Graphics2D g = (Graphics2D) gr;
		g.setColor(Color.black);
		g.setStroke(new BasicStroke(2.5f));

		for (int i = 1; i < 1 + Config.rows; i++) {
     // 行
			g.drawLine(Config.start, i * Config.SIZE, Config.end, i * Config.SIZE);
		}
		for (int i = 1; i < 1 + Config.columns; i++) {
     // 列
			g.drawLine(i * Config.SIZE, Config.start, i * Config.SIZE, Config.end);
		}
		for (int i = 0; i < rep.length; i++) {
     
			if (rep[i] != null) {
     
				rep[i].display(gr);
			}
		}
		g.fillOval(Config.start + 3 * Config.SIZE - 5, Config.start + 3 * Config.SIZE - 5, 10, 10);
		g.fillOval(Config.start + 11 * Config.SIZE - 5, Config.start + 3 * Config.SIZE - 5, 10, 10);
		g.fillOval(Config.start + 3 * Config.SIZE - 5, Config.start + 11 * Config.SIZE - 5, 10, 10);
		g.fillOval(Config.start + 11 * Config.SIZE - 5, Config.start + 11 * Config.SIZE - 5, 10, 10);
		g.fillOval(Config.start + 7 * Config.SIZE - 5, Config.start + 7 * Config.SIZE - 5, 10, 10);
	}

	/**
	 * 棋盘相关配置
	 * 
	 * @author mo
	 *
	 */
	public interface Config {
     
		public static final int start = 40;// 棋盘开始坐标(x,y)相同为start
		public static final int end = 640;// 棋盘结束坐标x,y相同为end
		public static final int rows = 16;// 横向条数
		public static final int columns = 16;// 竖向条数
		public static final int CHESS_SIZE = 35;// 棋子直径
		public static final int SIZE = 40;// 单元格大小

	}

	/**
	 * 清空棋盘
	 */

	public void clearChesses() {
     
		this.initchesses();
		for (int i = 0; i < rep.length; i++) {
     
			rep[i] = null;
		}
		this.paint(this.getGraphics());

	}

}
  • 监听器:监听各个按钮,监听鼠标,判断棋子位置是否正确,实现重绘棋子
package com.goband;

import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;

import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTextArea;

import com.goband.Checkerboard.Config;
/**
 * 五子棋监听器
 * @author mo
 *
 */

public class DrawListener implements MouseListener,ActionListener{
     
	 private int count=0,flag_start=0,flag_winner=0;
	 private Graphics g;
	 private Repaint []rep;
	 private int de=0;
	 private int [][] chesses;
	 private int [][] back=new int[256][2];
	 private Check check;
	 private Checkerboard board;
	 private String name;
	 private JFrame finshjf=new JFrame();
	 private JTextArea txt=new JTextArea();
	 
	 public void setGraphics(Graphics g){
     
		 this.g=g;
		 
	 }
	 /**
	  * 为了实现棋盘的重绘
	  */
	 public void setCheckerboard(Checkerboard c){
     
		 this.board=c;
		 txt=board.getEast();
	 }
	 public void setChesses(int [][]c){
     
		 this.chesses=c;
		 check=new Check(this.chesses);
	 }
	 /**
	  * 重绘数组
	  * @param rep
	  */
	 public void setrepaint(Repaint [] rep){
     
		 this.rep=rep;
	 }
	 public void actionPerformed(ActionEvent e){
     
		 name=e.getActionCommand();
		 if(name.equals("开始游戏")||name.equals("重新开始")){
     
			 board.clearChesses();
			 flag_start=1;
			 flag_winner=0;
			 count=0;
			 finshjf.setVisible(false);
			 txt.append("游戏开始"+"\n");
		 }
		 if(name.equals("返回棋局")){
     
			finshjf.setVisible(false);
			txt.append("返回棋局"+"\n");
		 }
		 if(name.equals("悔棋")){
     
			 if(count<2)
				 txt.append("不能悔棋!"+"\n");
			 else txt.append("悔棋成功"+"\n");
			 this.rep[count-1]=null;
			 this.rep[count-2]=null;
			 chesses[back[count-1][0]][back[count-1][0]]=0;
			 chesses[back[count-1][0]][back[count-1][1]]=0;
			 chesses[back[count-2][0]][back[count-2][0]]=0;
			 chesses[back[count-2][0]][back[count-2][1]]=0;
			 count-=2;
			 board.paint(board.getGraphics());
		 }
	 }
	 /**
	  * 鼠标点击事件
	  * 放下棋子
	  */
	 public void mouseClicked(MouseEvent e){
     
		 int x0=e.getX();
		 int y0=e.getY();
		 
		 Image img_b=new ImageIcon("D:/learning/mydemo/Javaworkspace/rect/src/com/goband/黑棋.png").getImage();
		 Image img_w=new ImageIcon("D:/learning/mydemo/Javaworkspace/rect/src/com/goband/白棋.png").getImage();
		 
		/**
		 * 判断按下是否有效
		 */
		 if(flag_start==0) txt.append("请点击开始游戏"+"\n");
		 System.out.println(x0+" "+y0);
		 if((flag_start==1)&&((x0-Config.start)%Config.SIZE<Config.SIZE/3||
				 ((x0-Config.start)%Config.SIZE>2*Config.SIZE/3)&&
				 ((y0-Config.start)%Config.SIZE<Config.SIZE/3)||
				(y0-Config.start)%Config.SIZE>2*Config.SIZE/3)&&
				 (x0>Config.SIZE*2/3)&&(y0>Config.SIZE*2/3)){
     
			 /**
			  * x,y保存的是画笔需要绘制的界面坐标,先用坐标减去statr取个整
			  */
			 int x=Config.start+Config.SIZE*((x0-Config.start)/Config.SIZE);
			 int y=Config.start+Config.SIZE*((y0-Config.start)/Config.SIZE);
			 if(((x0-Config.start)%Config.SIZE>2*Config.SIZE/3)){
     
				  x=Config.start+Config.SIZE*(1+(x0-Config.start)/Config.SIZE);
				 
			 }
			 if(((y0-Config.start)%Config.SIZE>2*Config.SIZE/3)){
     
				 y=Config.start+Config.SIZE*(1+(y0-Config.start)/Config.SIZE);
			 }
			 /*
			  * x,y需减去start再除棋盘大小
			  * 对应棋盘数组的索引
			  */
			 int index_x=(x-Config.start)/Config.SIZE;
			 int index_y=(y-Config.start)/Config.SIZE;
			 if(chesses[index_x][index_y]==0){
     
				 System.out.println(index_x+" "+index_y);
				 
				 if(count%2==0){
     
					 txt.append("当前为白棋"+"\n");
				
					 g.drawImage(img_b, x-Config.CHESS_SIZE/2, y-Config.CHESS_SIZE/2, null);
					 chesses[index_x][index_y]=1;
					 back[count][0]=index_x;
					 back[count][1]=index_y;
					 if(check.checkColumn(index_x, index_y)>=5||check.checkRow(index_x, index_y)>=5
							 ||check.checkIncleft(index_x, index_y)>=5||check.checkIncright(index_x, index_y)>=5)
					 {
      
						System.out.println("黑胜");
						txt.append("游戏结束"+"\n");
						txt.append("黑棋胜利"+"\n");
					 	flag_winner=1;
					 }
				 }else{
     
					 txt.append("当前为黑棋"+"\n");
					 g.drawImage(img_w, x-Config.CHESS_SIZE/2, y-Config.CHESS_SIZE/2, null);
					 chesses[index_x][index_y]=-1;
					 back[count][0]=index_x;
					 back[count][1]=index_y;
					 if(check.checkColumn(index_x, index_y)>=5||check.checkRow(index_x, index_y)>=5
							 ||check.checkIncleft(index_x, index_y)>=5||check.checkIncright(index_x, index_y)>=5)
					 {
      
						 System.out.println("白胜");
						 txt.append("游戏结束"+"\n");
						 txt.append("白棋胜利"+"\n");
						 flag_winner=-1;
					 }
					 
					
				 }
				
				 Repaint r=new Repaint(x-Config.CHESS_SIZE/2, y-Config.CHESS_SIZE/2, count%2==0?img_b:img_w);
				 rep[de++]=r;
				 count++;
				
			 }
			 System.out.println("name:"+name);
			 if(flag_winner!=0||count==225) {
     
				 finshjf=this.finshShow();
				 finshjf.setVisible(true);
			 }
			 
	 	}
		 
	 };
	 /**
		 * 一方胜利后显示
		 */
		public JFrame finshShow(){
     
			flag_start=0;
			board.initchesses();
			JFrame jf=new JFrame("Finsh Game!");
			jf.setSize(200, 120);
			jf.setDefaultCloseOperation(2);
			//关闭方法 :销毁不结束
			jf.setLocationRelativeTo(null);
			//居中显示
			jf.setLayout(new FlowLayout());
			//流式布局
			JButton yes=new JButton("重新开始");
			JButton no=new JButton("返回棋局");
			jf.add(yes);
			jf.add(no);
			yes.addActionListener(this);
			no.addActionListener(this);
			
			JLabel jl=new JLabel();
			if(flag_winner==1)
	 			 jl=new JLabel(" 黑棋胜利! ",new ImageIcon("D:/learning/mydemo/Javaworkspace/rect/src/com/goband/黑棋.png"), 0);
			 else if(flag_winner==-1)
				 jl=new JLabel(" 白棋胜利! ",new ImageIcon("D:/learning/mydemo/Javaworkspace/rect/src/com/goband/白棋.png"), 0);
			 else if(flag_winner==0&&count==225)
				 jl=new JLabel(" 平局! ");
	 
	 		jf.add(jl);
			return jf;
		}
		
	 	
	    public void mousePressed(MouseEvent e){
     

	    };
	   
	    public void mouseReleased(MouseEvent e){
     

	    };
	    public void mouseEntered(MouseEvent e){
     };

	   
	    public void mouseExited(MouseEvent e){
     };
}

  • 重绘:保存棋子
package com.goband;

import java.awt.Graphics;
import java.awt.Image;


public class Repaint {
     
	private int x1,y1;
	private Image c;
	public Repaint(int x1,int y1,Image c){
     
		this.x1=x1;
		this.y1=y1;
		this.c=c;
	}
	public void display(Graphics g){
     
		 g.drawImage(c, x1, y1, null);
	}
}

  • 检查是否有五连珠
package com.goband;

import com.goband.Checkerboard.Config;

public class Check {
     
	private int [][] chesses;
	public Check(int [][]c){
     
		this.chesses=c;
	}
	/**
	 *判断横向是否有5个相连的棋子
	 * @param x
	 * @param y
	 * @return
	 */
	public int checkRow(int x,int y){
     
		int count =0 ;
		/**
		 * 往右判断
		 */
		for(int i=x+1;i<chesses.length;i++){
     
			if(chesses[i][y]==chesses[x][y])
				count++;
			else
				break;
		}
		/*
		 * 往左判断
		 */
		for(int i=x;i>=0;i--){
     
			if(chesses[i][y]==chesses[x][y])
				count++;
			else
				break;
		}
		return count;
	}
	/**
	 * 判断纵向是否有5个相连的棋子
	 * @param x
	 * @param y
	 * @return
	 */
	public int checkColumn(int x,int y){
     
		int count =0 ;
		/**
		 * 往下判断
		 */
		for(int i=y+1;i<chesses.length;i++){
     
			if(chesses[x][i]==chesses[x][y])
				count++;
			else
				break;
		}
		/*
		 * 往上判断
		 */
		for(int i=y;i>=0;i--){
     
			if(chesses[x][i]==chesses[x][y])
				count++;
			else
				break;
		}
		return count;
	}
	/**
	 * 判断斜向左是否有5个相连的棋子
	 * @param x
	 * @param y
	 * @return
	 */
	public int checkIncleft(int x,int y){
     
		int count =0 ;
		/**
		 * 往左下判断
		 */
		for(int i=y+1,j=x-1;j>=0&&i<Config.columns;j--,i++){
     
			if(chesses[j][i]==chesses[x][y])
				count++;
			else
				break;
		}
		/**
		 * 往右上判断
		 */
		 
		for(int i=y,j=x;j<Config.rows&&i>=0;j++,i--){
     
			if(chesses[j][i]==chesses[x][y])
				count++;
			else
				break;
		}
		return count;
	}
	/**
	 * 判断右斜线是否有5个相连棋子
	 * @param x
	 * @param y
	 * @return
	 */
	public int checkIncright(int x,int y){
     
		int count =0 ;
		/**
		 * 往右下判断
		 */
		for(int i=y+1,j=x+1;j<Config.rows&&i<Config.columns;j++,i++){
     
			if(chesses[j][i]==chesses[x][y])
				count++;
			else
				break;
		}
		/**
		 * 往左上判断
		 */
		 
		for(int i=y,j=x;j>=0&&i>=0;j--,i--){
     
			if(chesses[j][i]==chesses[x][y])
				count++;
			else
				break;
		}
		return count;
	}
	
}

  • 主函数
package com.goband;

public class Main {
     
	public static void main(String [] args){
     
		Checkerboard c=new Checkerboard();
		c.initchesses();
		c.chshow();
		
	}
}

六、新用到的知识点

新学了一个显示方法,解决如何在用户界面显示数据的问题!

  • 使用的是JTextArea这个组件,在javax.swing.JTextArea包中
  • 添加滚动条,记录全部数据
  • 每次要打印新的信息只需要用append()方法:
						txt.append("游戏结束"+"\n");
						txt.append("白棋胜利"+"\n");

注意使用滚动条的方法:

		private JTextArea txt = new JTextArea(35, 20);//后面是大小,行数列数
		......
		/*
		 * 东
		 */
		JPanel east = new JPanel();
		east.setPreferredSize(new Dimension(330, 0));
		jf.add(east, BorderLayout.EAST);
		JLabel jl = new JLabel(" = 游戏信息提示 = ");
		east.add(jl);
		/**
		 * 添加滚动条
		 */
		JScrollPane js = new JScrollPane();
		east.add(js);
		js.setBounds(10, 10, 30, 600);
		txt.setBounds(10, 10, 30, 600);
		js.setViewportView(txt);// 添加到滚动窗格
		// 下句会重复添加
		// east.add(txt);
  • 把画的棋子用图标(ImageIcon)表示,图标图片像素要跟原来棋子的直径一样:35*35
 		Image img_b=new ImageIcon("D:/learning/mydemo/Javaworkspace/rect/src/com/goband/黑棋.png").getImage();
		Image img_w=new ImageIcon("D:/learning/mydemo/Javaworkspace/rect/src/com/goband/白棋.png").getImage();
		g.drawImage(img_b, x-Config.CHESS_SIZE/2, y-Config.CHESS_SIZE/2, null);
		g.drawImage(img_w, x-Config.CHESS_SIZE/2, y-Config.CHESS_SIZE/2, null);  								

因为绘制的是图片,所以重绘函数也要响应修改,不再是绘制圆形

package com.goband;

import java.awt.Graphics;
import java.awt.Image;


public class Repaint {
     
	private int x1,y1;
	private Image c;
	public Repaint(int x1,int y1,Image c){
     
		this.x1=x1;
		this.y1=y1;
		this.c=c;
	}
	public void display(Graphics g){
     
		 g.drawImage(c, x1, y1, null);
	}
}

下一篇写人机对战,用到的是评分法

你可能感兴趣的:(Java练习,五子棋,java)