JAVA 井字棋

有时候我们可以用java编写一些小游戏,比如井字棋,这是一个很简单的程序,如图效果;

我们可以将它分为棋子,棋盘,框架启动类表示;

首先我们来编写棋子类,棋子类里有棋子的坐标和形状的表示,

用1表示圆圈,2表示方框

public class Chess {
	private int x;
	private int y;//棋子的索引
	private int form;//棋子的形状,1是圆圈,2是方框
	public Chess(int x,int y,int form) {//构造函数
		this.x=x;
		this.y=y;
		this.form=form;
	}
	public int getX() {//get函数
		return x;
	}
	public int getY() {
		return y;
	}
	public int getForm() {
		return form;
	}
}

接下来进行框架类的编写,创建一个类来继承JFrame类,用来启动游戏,放置面板对象,设置大小,设置标题,一个游戏少不了一些按钮,井字棋中有三个按钮,重新开始游戏按钮,悔棋按钮和退出游戏按钮,有按钮就有相应的事件处理,这时候我们需要一个类继承事件处理类,并将三个按钮和事件处理进行绑定;

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.*;
/*
 * 井字棋的主框架类
 * 
 */

public class ChessJFrame extends JFrame {
	private Chessbord chessbord;//声明一个棋盘对象
	private Button	restartgame;//声明一个重新开始按钮
	private Button	backgame;//声明一个悔棋按钮
	private Button	exitgame;//声明一个退出游戏按钮
	private JPanel jp;//面板对象,存放按钮
	public ChessJFrame() {//构造函数,对参数初始化
		setTitle("井字棋");//设置表题名称
		MyActLister ma=new MyActLister();//事件处理对象
		chessbord=new Chessbord();//棋盘对象
		restartgame=new Button("重新开始");//
		backgame=new Button("悔棋");
		exitgame=new Button("退出");//对三个按钮进行初始化
		jp=new JPanel();//面板对象
		jp.setLayout(new FlowLayout(FlowLayout.CENTER));//流式布局
		jp.add(restartgame);//
		jp.add(backgame);
		jp.add(exitgame);//将三个按钮添加到面板中
		add(jp,BorderLayout.SOUTH);//面板的位置
		restartgame.addActionListener(ma);
		backgame.addActionListener(ma);
		exitgame.addActionListener(ma);//将三个按钮事件源进行监听
		add(chessbord);//添加棋盘
		pack();//框架大小的自适应
		
	}
	
	public static void main(String[] args) {
		ChessJFrame jf=new ChessJFrame();//声明框架对象
		jf.setVisible(true);//可见
		jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//设置关闭方式
		jf.setLocationRelativeTo(null);//将面板置中
	}
	private class MyActLister implements ActionListener{//事件监听内部类
		@Override
		public void actionPerformed(ActionEvent e) {
			Object obj=e.getSource();//获取事件源
			if(obj==restartgame) {//重新开始事件
				System.out.println("重新开始");
				chessbord.restartgame();
			}
			else if(obj==backgame) {//悔棋事件
				System.out.println("悔棋");
				chessbord.backgame();
			}else if(obj==exitgame) {//退出游戏事件
				System.exit(0);
			}
			
		}	
	} 
	
}

最后一个棋盘类是最复杂的,也是最重要的一个类,它继承的JTable类和两个鼠标事件接口,棋盘类里有画图函数,判单输赢函数和鼠标处理事件,

先说画图函数,它对棋盘进行画图,对棋子进行画图,要想弄清楚其中的关系,画一个九宫格自己比划比划,

画图函数调用的画直线函数方法,画圆圈方法,画方框方法;

protected void paintComponent(Graphics g) {//画棋盘和棋子
		super.paintComponent(g);
		for(int i=0;i<=ROWS;i++) {//画棋盘的行数
			g.drawLine(MARGIN, MARGIN+i*GRID_SPAN, MARGIN+COLS*GRID_SPAN, MARGIN+i*GRID_SPAN);
		}
		for(int i=0;i<=COLS;i++) {//画棋盘的列数
			g.drawLine(MARGIN+i*GRID_SPAN, MARGIN, MARGIN+i*GRID_SPAN, MARGIN+ROWS*GRID_SPAN);
		}
		for (int i = 0; i < chessCount; i++) {//画棋子
			int form=chessList[i].getForm();//判断是什么形状
			int xpos=chessList[i].getX()*GRID_SPAN+MARGIN+20;//棋子的X的坐标
			int ypos=chessList[i].getY()*GRID_SPAN+MARGIN+20;//棋子的y的坐标
			if(form==1) {//画圆圈
				g.drawOval(xpos,ypos, GRID_SPAN/2, GRID_SPAN/2);
			}else if(form==2) {//画方框
				g.drawRect(xpos, ypos, GRID_SPAN/2, GRID_SPAN/2);
			}
		}
	}

接下来就是鼠标事件,鼠标事件又分为鼠标移动事件和鼠标按下事件,

鼠标移动事件用来设置鼠标光标的,当光标在棋盘外,所在位置有棋子时或游戏结束时设置为默认形状;

代码如下;

​
public void mouseMoved(MouseEvent e) {//鼠标移动事件
		int x1=(e.getX()-MARGIN)/GRID_SPAN;//棋子x的坐标
		int y1=(e.getY()-MARGIN)/GRID_SPAN;//棋子y的坐标
		if(x1<0||x1>ROWS-1||y1<0||y1>COLS-1||findchess(x1,y1)||gameover) {//判断鼠标光标是否在棋盘外,是否所在位置有棋子,是否游戏结束
			setCursor(new Cursor(Cursor.DEFAULT_CURSOR));//如果是,设置为默认形状
		}else {//如果不是,则设置为手形
			setCursor(new Cursor(Cursor.HAND_CURSOR));
		}
	}

​

鼠标按下事件,是用来下棋子的,并判断棋子是否胜利,是否平局;

代码如下;

public void mousePressed(MouseEvent e) {//鼠标按下事件
		if(gameover)//游戏结束不能下
			return ;
		int form=start?1:2;
		int xindex=(e.getX()-MARGIN)/GRID_SPAN;
		int yindex=(e.getY()-MARGIN)/GRID_SPAN;
		if(xindex<0||xindex>ROWS-1||yindex<0||yindex>COLS-1||findchess(xindex, yindex)) {
			return ;//在棋盘外,所在位置有棋子,不能下
		}
		Chess ch=new Chess(xindex,yindex,form);//棋子对象
		chessList[chessCount++]=ch;//存放在棋子对象数组里
		repaint();//重画
		if(win(xindex,yindex)) {//如果游戏胜利,输出以下信息
			String msg=String.format("恭喜 %s赢了",start?"圆圈":"方框");
			JOptionPane.showMessageDialog(this, msg);
			gameover=true;
		}else if(chessCount==ROWS*COLS&&gameover==false) {//如果棋子下满,并且没有胜利,输出以下信息
			String msg=String.format("旗鼓相当,加油");
			JOptionPane.showMessageDialog(this, msg);
			gameover=true;
		}
		start=!start;//改变形状
	}

最后是判断棋子是否胜利的代码,个人觉得这是最难的地方;

判断井字棋是否胜利时,当棋子个数小于5时不用判断棋子是否胜利,棋子数不够;

判断主对角线上的棋子时,根据棋子的坐标我们可以知道主对角线上的x坐标与y坐标是相等的;

接下来就是遍历棋子数组中的棋子是否有x坐标与Y坐标相等的;

有的话就判断是否和当前所下棋子是否相同,是的话,数目加一,当数目为三时,说明游戏中有棋子胜利;

根据这个关系写出可以下代码;

if(xindex==yindex) {//主对角线上的棋子
		for(int i=0;i<chessCount;i++) {
			if(chessList[i].getX()==chessList[i].getY()) {
				if(chessList[i].getForm()==x) {
					Count++;
				}else
					break;
			}
		}
		if(Count==3) {//判断连续棋子个数是否为三
			return true;
		}else {
			Count=0;
		}

判断反对角线上的棋子,放对角线的棋子x坐标和y坐标相加等于同一个数;

判断反对角线上的棋子原理和主对角线上的原理差不多,这里就不多说了;

if(xindex+yindex==2) {//反对角线上的棋子
		for(int i=0;i<chessCount;i++) {
		if(chessList[i].getX()+chessList[i].getY()==2) {
				if(chessList[i].getForm()==x) {
					Count++;
				}else
					break;
			}
		}
			if(Count==3) {//判断连续棋子数是否为三
				return true;
			}else {
				Count=0;
			}
		}

接下来就是判断棋子所在行和列是否有相同连续的三个棋子;

根据坐标关系我们可知;同一列的坐标,它们的x坐标值相等,同一行的坐标,它们的y坐标相等;

以列为例,我们可以根据所下棋子的索引来对棋子所在的列进行遍历;

拿出当前所下棋子的x索引值,然后遍历棋子对象中有没有和当前棋子x索引相同的值,有的话说明该棋子是和所下棋子在同一列

然后进行判断,同一列有没有连续相同的三个棋子;有的话则当前棋子胜利;

行和列的判断代码如下;

for(int i=0;i<chessCount;i++) {//判断所下棋子所在列有没有连续三个的棋子
			if(xindex==chessList[i].getX()) {
				if(chessList[i].getForm()==x) {
					Count++;
				}else
					break;
			}
		}
				if(Count==3) {//判断连续棋子数是否为三
					return true;
				}else {
					Count=0;
					}
		for(int i=0;i<chessCount;i++) {//判断棋子所在行有没有连续的三个棋子
		if(yindex==chessList[i].getY()) {
			if(chessList[i].getForm()==x) {
				Count++;
			}else
				break;
		}
		}
		if(Count==3) {//判断连续棋子数是否为三
			return true;
		}else {
			Count=0;
		}

棋盘类三个比较重要的部分已经说完了,接下来我会贴出完整的棋盘类代码,大家再接再厉;

​
import java.awt.*;
import java.awt.event.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;

import javax.swing.*;
/*
 * 井字棋的棋盘类
 * 棋盘类继承了面板类和鼠标事件接口
 */
public class Chessbord extends JPanel implements MouseListener,MouseMotionListener{
	private static int MARGIN=30;//面板与框架之间的间距
	private static int ROWS=3;//行数
	private static int COLS=3;//列数
	private static int GRID_SPAN=80;//网格间距
	Chess[] chessList=new Chess[ROWS*COLS];//棋子数组,存放棋子对象
	int chessCount=0;//棋子个数
	boolean start=true;//默认圆圈先下
	boolean gameover=false;//游戏结束
	public Chessbord() {//构造函数
		setBackground(Color.GRAY);//设置背景颜色
		addMouseListener(this);
		addMouseMotionListener(this);//添加鼠标事件
		
	}
	@Override
	protected void paintComponent(Graphics g) {//画棋盘和棋子
		super.paintComponent(g);
		for(int i=0;i<=ROWS;i++) {//画棋盘的行数
			g.drawLine(MARGIN, MARGIN+i*GRID_SPAN, MARGIN+COLS*GRID_SPAN, MARGIN+i*GRID_SPAN);
		}
		for(int i=0;i<=COLS;i++) {//画棋盘的列数
			g.drawLine(MARGIN+i*GRID_SPAN, MARGIN, MARGIN+i*GRID_SPAN, MARGIN+ROWS*GRID_SPAN);
		}
		for (int i = 0; i < chessCount; i++) {//画棋子
			int form=chessList[i].getForm();//判断是什么形状
			int xpos=chessList[i].getX()*GRID_SPAN+MARGIN+20;//棋子的X的坐标
			int ypos=chessList[i].getY()*GRID_SPAN+MARGIN+20;//棋子的y的坐标
			if(form==1) {//画圆圈
				g.drawOval(xpos,ypos, GRID_SPAN/2, GRID_SPAN/2);
			}else if(form==2) {//画方框
				g.drawRect(xpos, ypos, GRID_SPAN/2, GRID_SPAN/2);
			}
		}
	}

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

	@Override
	public void mouseMoved(MouseEvent e) {//鼠标移动事件
		int x1=(e.getX()-MARGIN)/GRID_SPAN;//棋子x的坐标
		int y1=(e.getY()-MARGIN)/GRID_SPAN;//棋子y的坐标
		if(x1<0||x1>ROWS-1||y1<0||y1>COLS-1||findchess(x1,y1)||gameover) {//判断鼠标光标是否在棋盘外,是否所在位置有棋子,是否游戏结束
			setCursor(new Cursor(Cursor.DEFAULT_CURSOR));//如果是,设置为默认形状
		}else {//如果不是,则设置为手形
			setCursor(new Cursor(Cursor.HAND_CURSOR));
		}
	}
	private boolean findchess(int x1, int y1) {//判断所在位置是否有棋子
		for (Chess chess : chessList) {
			if(chess!=null&&chess.getX()==x1&&chess.getY()==y1) {
				return true;
			}
		}
		return false;
	}
	@Override
	public void mouseClicked(MouseEvent e) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void mousePressed(MouseEvent e) {//鼠标按下事件
		if(gameover)//游戏结束不能下
			return ;
		int form=start?1:2;
		int xindex=(e.getX()-MARGIN)/GRID_SPAN;
		int yindex=(e.getY()-MARGIN)/GRID_SPAN;
		if(xindex<0||xindex>ROWS-1||yindex<0||yindex>COLS-1||findchess(xindex, yindex)) {
			return ;//在棋盘外,所在位置有棋子,不能下
		}
		Chess ch=new Chess(xindex,yindex,form);//棋子对象
		chessList[chessCount++]=ch;//存放在棋子对象数组里
		repaint();//重画
		if(win(xindex,yindex)) {//如果游戏胜利,输出以下信息
			String msg=String.format("恭喜 %s赢了",start?"圆圈":"方框");
			JOptionPane.showMessageDialog(this, msg);
			gameover=true;
		}else if(chessCount==ROWS*COLS&&gameover==false) {//如果棋子下满,并且没有胜利,输出以下信息
			String msg=String.format("旗鼓相当,加油");
			JOptionPane.showMessageDialog(this, msg);
			gameover=true;
		}
		start=!start;//改变形状
	}

	private boolean win(int xindex,int yindex) {//判断游戏是否胜利
		if(chessCount<5) {//当棋子小于5时不用判断
			return false;
		}
		int x=start?1:2;//判断棋子形状
		int Count=0;//统计连续棋子数
		if(xindex==yindex) {//主对角线上的棋子
		for(int i=0;i<chessCount;i++) {
			if(chessList[i].getX()==chessList[i].getY()) {
				if(chessList[i].getForm()==x) {
					Count++;
				}else
					break;
			}
		}
		if(Count==3) {//判断连续棋子个数是否为三
			return true;
		}else {
			Count=0;
		}
		}
		if(xindex+yindex==2) {//反对角线上的棋子
		for(int i=0;i<chessCount;i++) {
		if(chessList[i].getX()+chessList[i].getY()==2) {
				if(chessList[i].getForm()==x) {
					Count++;
				}else
					break;
			}
		}
			if(Count==3) {//判断连续棋子数是否为三
				return true;
			}else {
				Count=0;
			}
		}
		for(int i=0;i<chessCount;i++) {//判断所下棋子所在列有没有连续三个的棋子
			if(xindex==chessList[i].getX()) {
				if(chessList[i].getForm()==x) {
					Count++;
				}else
					break;
			}
		}
				if(Count==3) {//判断连续棋子数是否为三
					return true;
				}else {
					Count=0;
					}
		for(int i=0;i<chessCount;i++) {//判断棋子所在行有没有连续的三个棋子
		if(yindex==chessList[i].getY()) {
			if(chessList[i].getForm()==x) {
				Count++;
			}else
				break;
		}
		}
		if(Count==3) {//判断连续棋子数是否为三
			return true;
		}else {
			Count=0;
		}
		
     return false;//否则返回false
		
	}
	@Override
	public void mouseReleased(MouseEvent e) {
		
	}

	@Override
	public void mouseEntered(MouseEvent e) {
		
	}

	@Override
	public void mouseExited(MouseEvent e) {
		// TODO Auto-generated method stub
		
	}
	
	public void restartgame() {//重新开始函数
		for (int i = 0; i < chessList.length; i++) {
			chessList[i]=null;
		}
		chessCount=0;
		gameover=false;
		start=true;//一切恢复为初始状态
		repaint();//重画
	}
	public void backgame() {//悔棋函数
		if(gameover) {//如果游戏结束,不能悔棋
			return ;
		}
		if(chessCount==0) {//如果棋子个数为0,不能悔棋
			return ;
		}
		chessList[chessCount-1]=null;
		chessCount--;
		start=!start;//改变形状
		repaint();//重画
	}
	
	public Dimension getPreferredSize() {//画矩形面板
		return new Dimension(MARGIN*2+ROWS*GRID_SPAN,MARGIN*2+COLS*GRID_SPAN);
	}
	
}

​

井字棋代码到此结束;

猜你喜欢

转载自blog.csdn.net/weixin_41946004/article/details/88367582