Java五子棋

提示:本人大二时的java大作业,当时没有学数据库,只是学到界面哪里,所以做出的条件有限,哈哈,看看就好,有帮助了,就拿走,不谢!

Java五子棋


前言

主要就是涉及到java界面编程,实现Runable接口重写run方法,实现多线程,来控制计时,另外加一些java基本语法的使用。


一、项目结构

在这里插入图片描述

二、代码

代码如下(示例):
FiveGame.java

package Wuziqi;

import javax.swing.*;//JFrame  、 JOptionPane引入
import java.awt.*;
import java.awt.event.MouseEvent;//鼠标事件 用到getX,getY
import java.awt.event.MouseListener;//鼠标监听
import java.awt.image.BufferedImage;
//JFrame是一个底层的容器
public  class FiveGame extends JFrame implements MouseListener,Runnable {
    //继承 JFrame类,实现 MouseListener接口 和 Runnable接口
    // 使用实现接口Runnable的对象来创建线程
    // 获取显示器屏幕大小

    int width = Toolkit.getDefaultToolkit().getScreenSize().width;
    int height = Toolkit.getDefaultToolkit().getScreenSize().height;

    int x, y;  // 定义鼠标的坐标

    int[][] allChess = new int[17][17];   // 用数组来保存棋子,0表示无子,1表示黑子,2表示白子
    boolean isblack= true;   //用来表示黑子还是白子, true表示黑子   false表示白子
    boolean canPlay = true;   // 用来表示当前游戏是否结束
    String message = "黑方先行";

    String blackMessage = "无限制";
    String whiteMessage = "无限制";

    int MaxTime=0;//计时器 - 倒计时 设置的时长
    Thread t= new Thread(this);//创建线程
    int blackT=0;//记录黑方和白方的剩余时间
    int whiteT=0;

    //保存棋谱,记录双方每一步落子的位置
    int[] chessX = new int[255];
    int[] chessY = new int[255];
    int countX, countY;

   //定义标志位 游戏胜负已定 不可执行悔棋
   boolean flag1=true;
  //未落子时不可执行悔棋,标志位
   boolean flag2=false;

    public FiveGame() {
        //标题
        this.setTitle("简易版五子棋");
        //窗体大小
        this.setSize(600, 600);
        //窗体弹出位置始终保证在屏幕中央
        this.setLocation((width - 500) / 2, (height - 500) / 2);
        //JFrame执行关闭操作时,将退出程序
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        //设置窗口不可改变,固定窗口大小
        this.setResizable(false);
        //使窗体可见
        this.setVisible(true);
        // 刷新页面,并再调用paint()方法
        this.repaint();
        //打开鼠标事件(按下、释放、单击、进入或离开)的侦听器接口
        this.addMouseListener(this);
        //开启线程-开始时线程挂起
        t.start();
        t.suspend();
    }

   /*
    对于轻量级组件,一般是重写 paint 方法以快速的绘制组件,但是对于重量及组件,由于重新绘制时间长,容易产生闪烁的现象,
    所以一般是采用重写 update 方法,利用双缓冲图片来解决闪烁的问题。
    repaint -> update -> paint
    repaint,update和paint这三个方法在Component中定义,由于awt,swing组件都直接或间接继承自Component,
    所以几乎所有的awt,swing组件都有这三个方法.
    */
    public void paint(Graphics g) {
        //双缓冲流,防止屏闪(将信息都缓存在内存信息中,统一画出)
        BufferedImage buf = new BufferedImage(600, 600, BufferedImage.TYPE_INT_RGB);
        // 创建画笔 在已存在的窗体或控件上绘图
        Graphics g1 = buf.createGraphics();
        //设置填充颜色 画一个着色块
        g1.setColor(new Color(0, 169, 158));
        //画突出矩形
        g1.fill3DRect(40, 80, 400, 400, true);

        /*绘制棋盘16x16*/
        for (int i = 0; i <= 16; i++) {
            //设置画笔颜色
            g1.setColor(Color.WHITE);
            g1.drawLine(40, 80 + i * 25, 400 + 40, 80 + i * 25);  //画棋盘横线
            g1.drawLine(40 + i * 25, 80, 40 + i * 25, 400 + 80);  //画棋盘竖线
        }
        //设计字体显示效果 (String 字体,int 风格,int 字号)
        g1.setFont(new Font("黑体", Font.BOLD, 15));//黑体 加粗 15号
        g1.drawString("游戏信息:" + message, 50, 60);

        //        画黑方时间与白方时间字符串的边框(画矩形)
        g1.drawRect(30, 500, 180, 40);
        g1.drawRect(250, 500, 180, 40);

        g1.setFont(new Font("宋体", 0, 17));
        g1.drawString("黑方时间: " + blackMessage, 40, 520);
        g1.drawString("白方时间: " + whiteMessage, 260, 520);

        //绘制按钮框
        g1.setFont(new Font("宋体", 0, 15));
        g1.drawRect(500, 70, 80, 35);
        g1.drawString("重新开始", 510, 95); //重新开始按钮

        g1.drawRect(500, 150, 80, 35);
        g1.drawString("游戏设置", 510, 175); //游戏设置按钮

        g1.drawRect(500, 230, 80, 35);
        g1.drawString("游戏说明", 510, 255); // 游戏说明按钮

        g1.drawRect(500, 310, 80, 35);
        g1.drawString("退出游戏", 510, 335);  // 退出游戏按钮

        g1.drawRect(500, 380, 80, 35);
        g1.drawString("  悔棋 ", 510, 405);  // 悔棋

        g1.drawRect(500, 460, 80, 35);
        g1.drawString("  认输 ", 510, 485);  // 认输

        g1.drawRect(500, 520, 80, 35);
        g1.drawString("  关于 ", 510, 545);  // 关于


        //画棋子
        for (int i = 0; i <=16; i++) {
            for (int j = 0; j <=16; j++) {
                //画实心黑子
                if (allChess[i][j] == 1) {
                    int tempX = i * 25 + 40;
                    int tempY = j * 25 + 80;
                    g1.setColor(Color.BLACK);
                    // fillOval 绘制实心椭圆 默认黑色填充
                    g1.fillOval(tempX - 10, tempY - 10, 16, 16);
                }
                //画实心白子
                if (allChess[i][j] == 2) {
                    int tempX = i * 25 + 40;
                    int tempY = j * 25 + 80;
                    g1.setColor(Color.WHITE);//设置填充颜色
                    g1.fillOval(tempX - 10, tempY - 10, 16, 16);
                    g1.setColor(Color.BLACK); //颜色恢复
                    g1.drawOval(tempX - 10, tempY - 10, 16, 16);
                }
            }
        }
//        将缓存的内容绘制到窗体上
        g.drawImage(buf, 0, 0, this);
    }


    public void mousePressed(MouseEvent e) {
      //        鼠标按下
        if (canPlay) {
            x = e.getX();
            y = e.getY();  // 用来获取鼠标坐标
            if (x >= 40 && x <= 450 && y >= 80 && y <= 480) {
                //让鼠标在棋盘范围内 计算位置在几行几列
                if((x-40)%25>12){
                    x=(x-40)/25 + 1;
                }else {
                    x = (x-40)/25;
                }
                if((y-80)%25>12){
                    y=(y-80)/25 + 1;
                }else {
                    y=(y-80)/25;
                }
                /*该位置未落子时才可以落子*/
                if(allChess[x][y]==0) {
                    flag2=true;
                    chessX[countX++] = x;
                    chessY[countY++] = y;
                    if (isblack) {
                        allChess[x][y] = 1;
                        isblack = false;
                        message="白方落子";
                    } else {
                        allChess[x][y] = 2;
                        isblack = true;
                        message="黑方落子";
                    }
                    // 刷新页面,并再调用paint()方法
                    this.repaint();
                    boolean win=this.panwin();
                    if(win){
                        t.suspend();//游戏结束,停止计时,线程挂起
                        JOptionPane.showMessageDialog(this,"游戏结束,"+(allChess[x][y]==1?"黑方":"白方")+"获胜");
                        canPlay=false;//不可落子
                        flag1=false;//不可悔棋
                    }
                }
            }
        }
//        重新开始
        if(e.getX()>=499&&e.getX()<=580&&e.getY()>=68&&e.getY()<=110){
            int i = JOptionPane.showConfirmDialog(this, "是否重新开始游戏?");
            if (i==0){//是
                flag2=false;
                flag1=true;//可悔棋
                canPlay=true;//可落子
                //清空棋盘  保存棋子的数组置为0
                allChess=new int[17][17];
                //还原初值
                message="黑方先行";
                //黑方落子
                isblack=true;
                /*时间*/
                blackT=MaxTime;
                whiteT=MaxTime;
                if(MaxTime>0){
                    blackMessage = MaxTime/3600+":"+(MaxTime/60- MaxTime/3600*60)+":"+(MaxTime-MaxTime/60*60);
                    whiteMessage = MaxTime/3600+":"+(MaxTime/60- MaxTime/3600*60)+":"+(MaxTime-MaxTime/60*60);
                    t.resume();//启动线程,开始倒计时
                } else {
                    blackMessage = "无限制";
                    whiteMessage = "无限制";
                }
                /*调用repaint方法 重新绘图*/
                this.repaint();
            }
        }
//        游戏设置
        if(e.getX()>=499&&e.getX()<=580&&e.getY()>=150&&e.getY()<=180){
            String s = JOptionPane.showInputDialog(this, "请输入游戏时长限制(数值为非负整数," +
                    "0为无限时间,):(单位:秒)");
            try {
                MaxTime=Integer.parseInt(s);//将字符串转换为int
                if(MaxTime>=0){
                    int k=-1;
                    if(MaxTime>0)k = JOptionPane.showConfirmDialog(this, "游戏设置完成是否重新开始游戏?");
                    if(MaxTime==0)k = JOptionPane.showConfirmDialog(this, "是否确认将游戏时长设为无限制,并重新开始游戏?");
                    if(k==0){
                        flag2=false;//未落子时不可悔棋
                        flag1=true;//可悔棋
                        canPlay=true;//可落子
                        //清空棋盘  保存棋子的数组置为0
                        allChess=new int[17][17];
                        //还原初值
                        message="黑方先行";
                        //黑方落子
                        isblack=true;
                        /*时间*/
                        if(MaxTime>0){
                            blackT=MaxTime;
                            whiteT=MaxTime;
                            blackMessage = MaxTime/3600+":"+(MaxTime/60- MaxTime/3600*60)+":"+(MaxTime-MaxTime/60*60);
                            whiteMessage = MaxTime/3600+":"+(MaxTime/60- MaxTime/3600*60)+":"+(MaxTime-MaxTime/60*60);
                            t.resume();//启动线程,开始倒计时
                        }
                        if(MaxTime==0){
                            blackMessage = "无限制";
                            whiteMessage = "无限制";
                            /*调用repaint方法 重新绘图*/
                            t.suspend();//线程挂起
                        }
                        /*调用repaint方法 重新绘图*/
                        this.repaint();
                    }
                }
                else{
                    JOptionPane.showMessageDialog(this,"请输入正确信息,不能是负数");
                }
            }
            catch (NumberFormatException e1) {
                JOptionPane.showMessageDialog(this,"请输入正确信息!");
            }
        }
//        游戏说明
        if(e.getX()>=499&&e.getX()<=580&&e.getY()>=230&&e.getY()<=262){
            JOptionPane.showMessageDialog(this,"黑白棋手轮番执棋子,一" +
                    "方先在横向纵向或斜向上连成五子者获胜!");
        }
//        退出游戏
        if(e.getX()>=499&&e.getX()<=580&&e.getY()>=310&&e.getY()<=339){
            int result = JOptionPane.showConfirmDialog(this, "是否退出游戏?");
            if(result == 0){
                System.exit(0);
            }
        }
//        悔棋
        if(flag1&&flag2) {
            if (e.getX() >= 499 && e.getX() <= 580 && e.getY() >= 380 && e.getY() <= 415) {
                    int result = JOptionPane.showConfirmDialog(this,
                            (isblack ? "白方悔棋,黑方是否同意?" : "黑方悔棋,白方是否同意?"));
                    if (result == 0) {
                        allChess[chessX[--countX]][chessY[--countY]] = 0;
                        if (isblack) {
                            isblack = false;
                        } else {
                            isblack = true;
                        }
                        if(countX<=0){
                            flag2=false;
                        }
                        this.repaint();  //重绘棋盘
                    }
            }
        }
//        认输
        if(e.getX()>=499&&e.getX()<=580&&e.getY()>=462&&e.getY()<=490) {
            if (canPlay) {
                t.suspend();//挂起线程 点击按钮停止计时
                int i = JOptionPane.showConfirmDialog(this, "是否确认认输?");
                if (i == 0) {//是
                    if (isblack) {
                        JOptionPane.showMessageDialog(this, "黑方认输,游戏结束!");
                    } else {
                        JOptionPane.showMessageDialog(this, "白方认输,游戏结束!");
                    }
                    canPlay = false;
                } else {
                    t.resume();//不认输,继续计时
                }
            }
        }
//        关于
        if(e.getX()>=499&&e.getX()<=580&&e.getY()>=520&&e.getY()<=550){
            JOptionPane.showMessageDialog(this,"游戏作者:JAVA二组");
        }
    }
    /*判断输赢*/
     private boolean panwin(){//横向-纵向-斜向 五子连珠
        boolean flag=false;
         int count=0;
         int color=allChess[x][y];
        count=this.checkCount(1,0,color);//横向找
        if(count>=5){
            flag=true;
        }else {
            count=this.checkCount(0,1,color);//纵向找
            if(count>=5){
                flag=true;
            }else {
                count=this.checkCount(1,1,color);//左上--右下
                if(count>=5){
                    flag=true;
                }else {
                    count=this.checkCount(1,-1,color);//左下--右上
                    if(count>=5){
                        flag=true;
                    }
                }
            }
        }
         return flag;
     }
    /**
     * 检查棋盘中的五子棋是否连城五子
     */
    private int checkCount(int xChange , int yChenge ,int color){
        int count = 1;
        int tempX = xChange;
        int tempy = yChenge;  //保存初始值
        while((x+xChange)>=0&&(x+xChange)<=16&&(y+yChenge)<=16&&(y+yChenge)>=0&&color==allChess[x+xChange][y+yChenge]){
            count++;
            if(xChange != 0)  xChange++;
            if(yChenge != 0 ){
                if(yChenge > 0) {
                    yChenge++;
                }else {
                    yChenge--;
                }
            }
        }
        xChange = tempX;
        yChenge = tempy;   // 恢复初始值
        while((x-xChange)>=0&&(x-xChange)<=16&&(y-yChenge)<=16&&(y-yChenge)>=0&&color==allChess[x-xChange][y-yChenge]){
            count++;
            if(xChange != 0)  xChange++;
            if(yChenge != 0 ){
                if(yChenge > 0) {
                    yChenge++;
                }else {
                    yChenge--;
                }
            }
        }
        return count;
    }


    /*重写接口中的方法*/
    public void mouseClicked(MouseEvent e) {
        // TODO Auto-generated method stub  是注释语句,告诉看代码的人,这段代码自动生成。可写可不写
//  鼠标点击判断
    }

    public void mouseReleased(MouseEvent e) {
        // TODO Auto-generated method stub
//  鼠标抬起
    }

    public void mouseEntered(MouseEvent e) {
        // TODO Auto-generated method stub
//   鼠标进入
    }

    public void mouseExited(MouseEvent e) {
        // TODO Auto-generated method stub
// 鼠标离开
    }
    /*
     实现Runnable 接口
        1.定义类实现Runnable 接口
        2. 覆盖Runnable 接口中的run方法
        (将线程要运行的代码存放在该run方法中)
        3. 通过Thread 类建立线程对象
        4.将Runnale接口的子类对象作为实际的参数传递给Thread 类的构造函数
        (为什么要Runnable 接口的自欸对象传递给Thread的子类对象,所以要让线程去指定对象的run方法,就必须明确该run方法所属的对象)
        5.调用Thread 类的start方法开启线程并且调用Runnable 接口的子类的run 方法。*/
    @Override
    public void run() {
        if(MaxTime>0){
            while (true){
                if(isblack){
                    blackT--;
                    if(blackT==0){
                        canPlay=false;
                        blackMessage=0+":"+0+":"+0;//时间用完了
                        this.repaint();
                        JOptionPane.showMessageDialog(this,"黑方超时,游戏结束!");
                        flag1=false;
                        t.suspend();//线程挂起
                    }
                }else {
                    whiteT--;
                    if(whiteT==0){
                        canPlay=false;
                        whiteMessage=0+":"+0+":"+0;//时间用完了
                        this.repaint();
                        JOptionPane.showMessageDialog(this,"白方超时,游戏结束!");
                        flag1=false;
                        t.suspend();//线程挂起
                    }
                }
                blackMessage = blackT/3600+":"+(blackT/60- blackT/3600*60)+":"+(blackT-blackT/60*60);
                whiteMessage = whiteT/3600+":"+(whiteT/60- whiteT/3600*60)+":"+(whiteT-whiteT/60*60);
                this.repaint();
                try {//睡眠1000ms --1s
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

Main.java

package Wuziqi;
public class Main {
    public static void main(String[] args) {

        new FiveGame();

    }
}

三、效果图

当时写的,所有的按钮都不是真实的button组件,是画上去的。
在这里插入图片描述在这里插入图片描述
在这里插入图片描述


四、ppt和word文档

找不到上传的方法,此处就省略了。
部分方法的实现思路:

1.绘制棋盘的paint(Graphics g)方法
创建画笔主要使用方法:
setColor()设置画笔颜色
drawLine()画横线
drawString()将指定字符串在指定坐标位置显示
drawRect()画矩形框
fillOval()画实心椭圆
drawOval()画空心椭圆
用双层for循环绘画棋子(二维数组保存棋谱0-无子,1-黑字,2-白子)。

2.五子连珠判断
设计思路:每下一棋子,就以当前棋子为基准进行查找,查找一共分四个路径(横向、纵向、
左上到右下、右上到左下)。
横向查找先找右边(x++,y不变),再找左(x--,y不变)
纵向查找先找下方(x不变,y++),再找上方(x不变,y--)
左上到右下查找先找右下(x++,y++),再找左上(x--,y--)
右上到左下查找先找右上(x++,y--),再找左下(x--,y++)
定义一个整型变量count赋初值为1,一个整型变量color来保存棋子颜色(1为黑棋,2为白
棋),查找到颜色相同的变量值加一,最后查找完时,返回变量值,大于等于5时判赢。

3.悔棋按钮的实现
1).定义了一个二维数组来保存棋谱(0-无棋子,1-黑子,2-白字)。
2).在定义两个一维数组来保存当前所下棋子的坐标(几行几列)。
3).在执行悔棋功能时,就是将之前保存那个棋子位置所赋值变为0,表示无棋子,在调用repaint()方法,重回棋盘达到悔棋的效果。

4.游戏设置按钮的实现
1).用JOptionPane类中的showConfirmDialog()方法获取用户输入。
2).再用Integer.parseInt(s)方法将其转换为int型变量。
3).进行处理,转化为  时::秒(字符串型)。
4).其他定义的变量都恢复的初始状态
5).最后调用repaint()方法,将页面刷新。

5.线程实现倒计时
创建线程,让类实现Runnable 接口覆盖接口中的run方法(将线程要运行的代码存放在该run方法中)。
通过Thread 类建立线程对象,将Runnale接口的子类对象作为实际的参数传递给Thread 类的构造函数
调用Thread 类的start方法开启线程并且调用Runnable 接口的子类的run 方法。
线程用到了:
	t.start()//开启线程
	t.suspend()//线程挂起
	t.resume()//启动线程
	延时一秒:
    try {//睡眠1000ms --1s
           Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

总结

对于计算机专业的学生来说,每一次的大作业,还是挺让人兴奋的。用专门安排的时间,做自己想做的事,写自己想要的东西,在这写东西的过程中不断学习,提高自己的认知和知识体系。从遇到bug的无从入手,到网上搜搜改改的豁然开朗,从无到有,这种成就感还是让人很愉快的,哈哈。

  • 18
    点赞
  • 118
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值