当前位置:

首页 > 编程开发 > 怎么用Java实现中国象棋游戏

怎么用Java实现中国象棋游戏

一、界面下棋的棋盘首先要准备好,这就是一个合适大小合适比例合适位置的界面,然后在窗体上画上(没错drawLine的那种画上)n条直线和斜线,具体数值根据你的界面大小设置。这样画出的界面整齐好看~publicvoidpaint(Graphicsg){super.paint(g);//重写画图函数Fontf=newFont("微软雅黑",Font.BOLD,30);g.setFont(f);g.drawRect(60,50,500,560);//外圈g.drawRect(70,60,480,540);//内圈

本文目录

    一、界面

    下棋的棋盘首先要准备好,这就是一个合适大小合适比例合适位置的界面,然后在窗体上画上(没错drawLine的那种画上)n条直线和斜线,具体数值根据你的界面大小设置。这样画出的界面整齐好看~

    public void paint(Graphics g){
            
            super.paint(g);//重写画图函数
            Font f=new Font("微软雅黑",Font.BOLD,30);
            g.setFont(f);
            
            g.drawRect(60, 50, 500, 560);//外圈
            g.drawRect(70, 60, 480, 540);//内圈
            //横线部分
            int length=60;
            for(int i=0;i<9;i++){
                g.drawLine(70, length, 550, length);
                length+=60;
            }
            //中间汉字
            g.drawString("楚河", 160, 340);
            g.drawString("汉界", 400, 340);
            //竖线部分
            length=130;
            for(int i=0;i<7;i++){
                //上半部分竖线
                g.drawLine( length,60, length,300);
                //下半部分竖线
                g.drawLine( length,360, length,600);
                length+=60;
            }
            //上半部分九宫格斜线
            g.drawLine(250, 60, 370, 180);
             g.drawLine(370, 60, 250, 180);
             //下半部分九宫格斜线
             g.drawLine(250, 480, 370, 600);
             g.drawLine(250, 600, 370, 480);
         
        }

    二、按钮

    画好棋盘之后加上功能按钮,这个时候的功能暂时不考虑实现,可以根据喜好随意添加。这里推荐将按钮类型设置成数组,便于及时增删。

    //添加到面板上
            String[] type = {"开始游戏","重新开始","认 输","悔 棋"};
            for(int i=0;i

    怎么用Java实现中国象棋游戏

    这个时候你会发现在加按钮的地方贴的十分紧凑,我的解决办法是在这一块面板上再加一个面板设置为白色覆盖在上面,这样根据面板的流式布局按钮就会下移,调整空白面板的宽度可以改变按钮的位置。

    三、加棋子

    将找到的棋子图片加到棋盘交叉的位置上才是给棋盘注入灵魂,将所有的十四类图片加到package中以便程序之后可以在其他电脑上运行(这里推荐png格式,jpg格式会有方形边框)。
    接下来分三步走

    1.创建一个10行9列的整数数组,用来存储每个位置的数据;
    2.创建一个长度为14的Image数组,用来与棋子类型对应;
    3.遍历整数数组画出对应的棋子;

    这是棋盘的直观图,也就是我们的整数数组的初始值:

    怎么用Java实现中国象棋游戏

    将Image与棋子图片对应:

    //初始化给每个chess定义
            for(int k=0; k<14; k++){
                chess[k] = new ImageIcon(this.getClass().getResource((k+1)+".png")).getImage();
            }

    遍历画图:

    //根据棋盘布局
            for(int i=0;i0){
                        bg.drawImage(chess[place[i][j]-1], chessX+60*j, chessY+60*i, 50, 50, null); 
                    }
                }
            }

    四、实现棋子的移动

    通过函数获得鼠标拖动前后两个点所代表的棋盘上的位置,并将这两个位置的二维数组的值交换,然后重新绘图实现棋子的移动。

    int x1, y1, x2, y2;
    public void mousePressed(MouseEvent e) {
            
                x1 = e.getX();
                y1 = e.getY();
                x1 = getj(x1);
                y1 = geti(y1);
        }
        public void mouseReleased(MouseEvent e) {
            
                x2 = e.getX();
                y2 = e.getY();
                x2 = getj(x2);
                y2 = geti(y2);
        }
        //根据点的坐标得到其代表的位置,具体参数可以微调,我的格子是60x60大小
        public int getj(int x){
            return (x-50)/60;
        }
        public int geti(int y){
            return (y-40)/60;
        }

    这个时候遇到的状况就是你每次移动一次之后,整个界面都要重绘一次,而画面是直接画在窗体上的,数据会直接传到电脑硬件,这样一来画图速度就慢了,所以会出现每走一步界面就闪烁一次的情况,这种情况下,我们可以将画面先存在缓存中,就不经过硬件直接画出来,这样效率就可以明显提高。

    BufferedImage buffer = new BufferedImage(this.getWidth(), this.getHeight(), BufferedImage.TYPE_INT_ARGB);
    Graphics bg = buffer.getGraphics();
    
    //这个中间写的是你画界面的方法,也就是上面提到的paint方法内部
    //......
    
    //绘制缓存到窗体上
    g.drawImage(buffer, 0, 0, null);

    五、判断胜负

    率先吃掉对方帥或将的队伍获胜,写一个函数判断谁胜谁负显示胜局,同时将数据初始化为0,准备再来一局:
    (showMessageDialog方法可以直接跳出一个框)

    //判断游戏结束并显示胜局
    public void isWine() {
            
            System.out.println(place[y1][x1]+" "+place[y2][x2]);
            if (place[y2][x2]==7&&place[y1][x1]!=0) {
                place[y2][x2] = place[y1][x1];
                place[y1][x1] = 0;
                UI.repaint();
                JOptionPane.showMessageDialog(null, "黑方  胜利!");
                again();
            } else if(place[y2][x2]==14&&place[y1][x1]!=0) {
                place[y2][x2] = place[y1][x1];
                place[y1][x1] = 0;
                UI.repaint();
                JOptionPane.showMessageDialog(null, "红方  胜利!");
                again();
            }
        }
        //游戏结束时要重绘
        public void again(){
              for(int i=0; i

    六、按钮“开始游戏”和“重新开始”的实现

    加动作监听器

    public void actionPerformed(ActionEvent e) {
            
            type = e.getActionCommand();
            if("开始游戏".equals(type)||"重新开始".equals(type)){
                x=0;
                count = 1;//这里要把每次的走棋方刷新,认输时也需要刷新
                init();
                UI.repaint();
            }
        }
        //初始化place坐标
        public void init(){
    
            /*红兵 1.png
             *红炮 2.png
             *红車 3.png
             *红马 4.png
             *红相 5.png
             *红仕 6.png
             *红帥 7.png
             *黑卒 8.png
             *黑炮 9.png
             *黑車 10.png
             *黑马 11.png
             *黑象 12.png
             *黑士 13.png
             *黑将 14.png
             */
            
                
            for(int i=0;i

    这里的init函数是给整数二维数组初始化为开局后遍历可以加上棋子的状态。

    七、加规则

    //规定各个棋子的移动规则
    public boolean rule(int gi, int gj,int si, int sj){
            int x = place[gi][gj];
            int y = place[si][sj];
            int start, end;
            
            //判断为何种棋子
            //車:只能走直线
            if(x == 3||x == 10){
                
                if(gi != si&&gj != sj)    return false;
                else if(gi == si){
                    start = Math.min(gj, sj);
                    end = Math.max(gj, sj);
                    for(int m = 1; m < end - start; m++){
                        if(place[gi][start+m] != 0)    return false;
                    }
                    return true;
                }
                else if(gj == sj){
                    start = Math.min(gi, si);
                    end = Math.max(gi, si);
                    for(int m = 1; m < end - start; m++){
                        if(place[start+m][gj] != 0)    return false;
                    }
                    return true; 
                }
                else return true;
            }
            //马:走日,且某个位置不可以有棋子
            else if(x == 4||x == 11){
                //下
                if(si - gi == 2&&Math.abs(gj-sj) == 1&&place[gi+1][gj] == 0)    return true;
                //上
                else if(gi - si == 2&&Math.abs(gj-sj) == 1&&place[gi-1][gj] == 0)    return true;
                //右
                else if(sj - gj == 2&&Math.abs(gi-si) == 1&&place[gi][gj+1] == 0)    return true;
                //左
                else if(gj - sj == 2&&Math.abs(gi-si) == 1&&place[gi][gj-1] == 0)    return true;
                //否则不可以走
                else return false;
            }
            //相:走田,且不能过河
            else if(x == 5||x == 12){
                //左上
                if(gi - si == 2&&gj - sj == 2&&place[gi-1][gj-1] == 0){
                    
                    if((x == 5&&si >= 5)||(x == 12&&si < 5))    return true;
                    else return false;
                }
                //右上
                else if(gi - si == 2&&sj - gj == 2&&place[gi-1][gj+1] == 0){
                    
                    if((x == 5&&si >= 5)||(x == 12&&si < 5))    return true;
                    else return false;
                }
                //左下
                else if(si - gi == 2&&gj - sj == 2&&place[gi+1][gj-1] == 0){
                    
                    if((x == 5&&si >= 5)||(x == 12&&si < 5))    return true;
                    else return false;
                }
                //右下
                else if(si - gi == 2&&sj - gj == 2&&place[gi+1][gj+1] == 0){
                    
                    if((x == 5&&si >= 5)||(x == 12&&si < 5))    return true;
                    else return false;
                }
                else return false;
            }
            //士:斜着走不能出田字格
            else if(x == 6||x == 13){
                
                if(Math.abs(gj-sj)==1&&Math.abs(gi-si)==1){
                    
                    if(x == 6&&si >= 7&&sj >= 3&&sj <= 5)    return true;
                    else if(x == 13&&si <= 2&&sj >= 3&&sj <= 5)    return true;
                    else return false;
                }
                else return false;
            }
            //将:不能出田字格且不能会面
            else if(x == 7||x == 14){
                
                if((Math.abs(gj-sj)==1&&gi - si ==0)||(gj - sj ==0&&Math.abs(gi-si)==1)){
                    
                    if(x == 7&&si >= 7&&sj >= 3&&sj <= 5)    return true;
                    else if(x == 14&&si <= 2&&sj >= 3&&sj <= 5)    return true;
                    else return false;
                }
                else return false;
                
            }
            //炮:隔一个
            else if(x == 2||x == 9){
                
                //若要吃棋子,必须中间有且只有一枚棋子
                if(x*y!=0){
                    int t = 0;
                    if(gi == si){
                        for(int m = Math.min(gj, sj); m <= Math.max(gj, sj); m++){
                            if(place[gi][m] != 0)    t++;
                        }
                    }
                    else if(gj == sj){
                        for(int m = Math.min(gi, si); m <= Math.max(gi, si); m++){
                            if(place[m][gj] != 0)    t++;
                        }
                    }
                    if(t == 3)    return true;
                    
                }    
                    
                //若为不吃棋子的情况,中间不可以有其他棋子,且只能走直线
                else {
                    int t = 0;
                    if(gi == si){
                        for(int m = Math.min(gj, sj); m <= Math.max(gj, sj); m++){
                            if(place[gi][m] != 0)    t++;
                        }
                    }
                    else if(gj == sj){
                        for(int m = Math.min(gi, si); m <= Math.max(gi, si); m++){
                            if(place[m][gj] != 0)    t++;
                        }
                    }
                    if(t == 1) return true;
                    else return false;
                }
            }
            //兵:不能后退,且过了河才可以左右移动
            else if(x == 1||x == 8){
                //判断是否过河
                if(x == 1){
                    if(gi >=5){
                        if(gi - si == 1&&gj == sj)    return true;
                        else return false;
                    }
                    else if((gi - si == 1&&sj - gj == 0)||(gi - si == 0&&Math.abs(sj-gj) == 1))    return true;
                    else return false;
                }
                else if(x == 8){
                    if(gi <5){
                        if(si - gi == 1&&gj == sj)    return true;
                        else return false;
                    }
                    else if(((si - gi == 1&&sj - gj == 0))||(gi - si == 0&&Math.abs(sj-gj) == 1))    return true;
                    else return false;
                }
                else return false;
            }
            
            return false;
        }

    长长的一大串,这里对于炮和将需单独考虑,炮有直行和隔一个两种走法,需分开考虑,而将就更是麻烦

    //判断将是否会面
    public boolean meet(){
            int jiangi=0, jiangj=0, shuaii=0, shuaij=0, temp=0;
            for(int i=0; i<10; i++){
                for(int j=0; j<9; j++){
                    if(place[i][j]==7){
                        shuaii = i;
                        shuaij = j;
                    }
                    else if(place[i][j]==14){
                        jiangi = i;
                        jiangj = j;
                    }
                }
            }
            if(shuaij == jiangj){
                for(int i=jiangi+1; i

    八、轮次

    红黑轮流下棋
    我单独写了一个方法判断将是否会面,因为导致将会面的不仅是将自身的移动导致,还可能是其他棋子的移动,所以也是一个boolean函数,只有同时满足前一个函数以及这个函数返回的是不会面,才可以移动,移动时我定义了一个参数x记录局数,根据他的奇偶判断轮到哪一方走。这样就实现了象棋的规则。

    九、悔棋

    难免会有失误,加上悔棋功能更合适。
    我们在交换两个点的值时(或者吃子的情况)需记录下之前的值,然后当动作监听器监听到点击悔棋时又交换回来。
    一次只能悔棋一次,且刚开始时棋子没有移动不能悔棋。

    十、背景 及 提示

    加上自己挑的背景,并为了方便下棋,标注轮到哪一方。
    加背景可以用到画棋子同样的方法,所以要画在棋盘前面,防止被覆盖住。
    这样加上去又有一个很明显的问题,就是你每操作一次右边的按钮都会消失,被你的背景图覆盖了,这怎么办呢?
    于是乎,又总结出了三种方法:

    1.重写按钮的paint方法;
    2.将按钮以菜单的形式加在左上角;
    3.将按钮直接p在背景图上(截图),再画就可以了;

    此外你还可以在背景图上加上“当前下棋方”的字样,在边上显示当前下棋方将领的图片。
    这里借用了count参数并将其传到了监听器中,重写了构造函数。

    int count=1;
    
    if(listener.count==1){
                //画帥
            bg.drawImage(chess[6], 708, 322, 50, 50, null); 
        }else if(listener.count==-1){
                //画将
            bg.drawImage(chess[13], 708, 322, 50, 50, null); 
    }

    每下一子,count×(-1),以此标记是哪一方并画图。

    附一张成果图:

    怎么用Java实现中国象棋游戏

    本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系bd@zhengruan.com
    作者最新文章
    编程开发 Java
    相关文章 更多
    Java单元测试入门:使用JUnit 5编写第一个测试用例
    Java单元测试入门:使用JUnit 5编写第一个测试用例

    面向初学者的JUnit 5实用入门指南。从测试概念、Maven依赖配置,到手把手编写首个单元测试用例及断言验证,助你掌握Java单元测试的核心流程。

    Java变量未初始化报错解析:局部变量与成员变量默认值差异
    Java变量未初始化报错解析:局部变量与成员变量默认值差异

    深入解析Java中局部变量未初始化编译报错与成员变量自动赋予默认值的机制差异,涵盖JVM内存分配、编译期确定性赋值检查与实际排查修复方案。

    Java获取当前时间并格式化输出:DateTimeFormatter与SimpleDateFormat
    Java获取当前时间并格式化输出:DateTimeFormatter与SimpleDateFormat

    详细讲解在Java中获取当前系统时间并进行格式化输出的两种核心方案:基于Java 8全新日期时间API的DateTimeFormatter与传统遗留的SimpleDateFormat,包含完整代码示例、线程安全机制对比及生产选型建议。

    Java中final与static final常量的定义及初始化规则
    Java中final与static final常量的定义及初始化规则

    深入解析Java中final变量与static final常量的定义、内存语义、初始化时机及合法赋值路径,涵盖编译期常量优化与实战避坑指南。

    Java抽象方法与接口方法的区别及选型建议
    Java抽象方法与接口方法的区别及选型建议

    深入对比Java中抽象类抽象方法与接口方法的语法演进、设计意图及运行期差异,并提供清晰的架构选型决策流程与实战范例。

    Java实现HTTP请求与JSON解析的完整代码示例
    Java实现HTTP请求与JSON解析的完整代码示例

    本文提供Java标准库HttpClient发起HTTP GET/POST请求并结合Jackson进行JSON数据反序列化与序列化的完整代码示例与工程实践指南。

    Java递归出现StackOverflowError的原因分析及栈空间优化方案
    Java递归出现StackOverflowError的原因分析及栈空间优化方案

    详细分析Java递归调用出现StackOverflowError的原因,结合代码说明线程栈、栈帧和递归深度之间的关系,并介绍终止条件排查、迭代改写、减少栈帧开销以及通过-Xss调整JVM栈空间的方法。

    Java空指针异常(NPE)的代码排查与修复实例
    Java空指针异常(NPE)的代码排查与修复实例

    针对Java中常见的NullPointerException(NPE),本文从堆栈日志定位、典型代码触发场景、具体修复代码对比到防御体系进行系统讲解,提供实用且易上手的排查与修复方案。

    selenium上传文件教程本地文件路径设置方法
    selenium上传文件教程本地文件路径设置方法

    深入解析Selenium处理文件上传的技术细节,涵盖标准HTML控件的路径直接注入法与非标准控件的系统级模拟方案,提供Windows与Linux环境下的路径设置最佳实践及常见异常排查。

    大数据开发语言选型:Java对比Scala与Python的优缺点及适用场景
    大数据开发语言选型:Java对比Scala与Python的优缺点及适用场景

    针对大数据工程场景,系统对比Java、Scala与Python在执行性能、生态结合、开发效率和维护成本上的核心差异,给出清晰的架构选型依据与落地方案。

    查看更多
    精品专题 更多
    装机必备
    装机必备

    正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

    Windows
    Windows

    正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

    PDF教程
    PDF教程

    正软商城PDF教程频道提供PDF编辑、转换、合并、拆分、压缩及格式处理方法,同时介绍常用PDF软件和工具的使用技巧。

    Mac软件 更多
    讯飞智作 网页版
    讯飞智作 网页版
    Windows/ macOS/ Linux

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    火山引擎 网页版
    火山引擎 网页版
    Windows/ macOS/ Linux

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    Aperty macOS版
    Aperty macOS版
    macOS

    Aperty 是 Skylum 推出的 AI 人像修图软件,支持自然肤质修饰、瑕疵处理、面部与身体调整、预设及批量同步。

    WINDOWS 更多
    讯飞智作 网页版
    讯飞智作 网页版
    Windows/ macOS/ Linux

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    火山引擎 网页版
    火山引擎 网页版
    Windows/ macOS/ Linux

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    PixTV 网页版
    PixTV 网页版
    Windows

    PixTV 是一款在浏览器中使用的 AI 视觉内容创作平台,提供图片、视频、音频、文字生成、AI 分镜、局部重绘、提示词反推和无限画布等工具,适合短剧、漫剧、广告及社交媒体内容制作。