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);
}
}
井字棋代码到此结束;
标签:JAVA,鼠标,int,private,井字棋,棋子,new,public 来源: https://blog.csdn.net/weixin_41946004/article/details/88367582