美文网首页@future
Java小游戏·斗地主

Java小游戏·斗地主

作者: 不返y | 来源:发表于2023-11-04 21:59 被阅读0次

0.关于斗地主的游戏代码

注:本程序是按照哔哩哔哩上黑马的阶段练习做的游戏,但是只是套用部分其的部分素材,和原程序可能逻辑不一,且ai算法也可能不一,本程序采用的算法比较简单,基本上就是能出则出。

所以这个游戏代码实际上就是我的阶段练习作业,在此其实我也是通过复阅自己的代码在对此阶段进行一些复习与回顾。在此次代码中运用都是一些简单的图形化界面,在后面我会对我所有运用到的图形化界面的进行总结罗列出来它们的方法以及作用。

先直接来看一下程序运行的简单界面,再来开始介绍:

1.png

因为简书上使用md编辑器没有办法上传视频,,所以就只能直接开始了,如果感兴趣的可以看下文章最后的代码。当然,很大可能我写的有点问题,希望大家多多指出了。

1. 所使用的对象

Java是面向对象的一门语言,因此在编程中最重要的是清楚自己所写的对象具有哪些属性,下面我从牌类Poker的代码上开始介绍。

1.1 牌类Poker

我的牌类Poker继承了的是JLable,实际就相当于自己创建出了一个组件。目的是:

  1. 可以把牌在界面上显示出来
  2. 把牌当成是一个可以被点击可以被移动到对象;

但最其本质上就是创建了一个自定义组件。

public class Poker extends JLabel implements MouseListener {
    private static final String path = "DouDiZhu\\image\\poker";//牌对应的图片的文件夹地址
    private String ID;//牌名字
    private boolean faceUP;//牌面是否向上
    private Point point;//牌当前的位置
    private boolean top;//牌是否弹起
    private boolean canTop;//牌是否能弹起

后面的implement表示实现了一个接口,为鼠标监听,等后面在去提它。

首先先来看类的成员变量(有时候也叫做字段)。

成员变量

String path

第一个成员变量是private static final String path = "DouDiZhu\\image\\poker";//牌对应的图片的文件夹地址

它用来记录图片所在的文件夹,也就是牌的图片位置,也就是素材的位置(使用的是绝对路径)。

比如图片【♠A】,他在"DouDiZhu\\image\\poker\\1-1.png"的位置中。

2.png

使用一个常量记录下来的好处是方便后期的修改。试想一下,如果你的图片因为一些原因被迫要换个地方存放(移动到其他的地方),此时如果你在每一个调用图片的地方都写上"DouDiZhu\\image\\poker\\1-1.png"这么一串地址,你要修改多少地方,会不会很麻烦,恐怕甚至连知道要修改什么地方都不知道。而采用常量的方式,就可以更加方便的修改,而且你引用的时候也更加方便,只需要用你写的常量名就行,如上面的path

一般常量都是用public static final进行修饰的,但在此处我只需要在牌Poker这个类中使用,所以使用了private修饰。

以下是我个人总结的一些经验,在设计成员变量的时候如果不是要定义一个常量,尽量都设置成private(私有的),并且引用变量的时候尽量都通过对应的get以及set方法去进行修改和设置,并且如果不是工具类的话,尽量减少使用static方法,避免出现同一个类的两个对象之间有很深的联系(会互相干扰)的情况。

String ID

你也许会注意到,上面我对于牌的图片命名----1-1.png
其实这个就是为了方便对牌进行管理而命名的,每一个数字都有他自己的含义。第一个 ‘ 1 ’ 表示他是黑桃,第二个 ‘ 1 ’ 表示他是 1 这张牌,合起来 ’ 1-1 ‘ 黑桃A 就是这张牌的 ID,private String ID;,它可以完全可以用来描述这个对象是那一张牌。

对于这个变量,相关的方法有:

1.get和set方法,基本上是每个变量的基础方法了,就是获取与设置变量的值,后边的变量都不在提及

public String getID() {
        return ID;
    }
public void setID(String ID) {
        this.ID = ID;
    }

2.getNumber方法

public int getNumber() {
        return Integer.parseInt(getID().substring(2));
    }

就是把ID中的数字单独获取出来了。

getID().substring(2)方法是截取字符串中下标为2的往后所有,刚好是数字的位置。

Integer.parseInt()方法是用来把字符串变成数字,如果传入的不是数字就会抛出错误

3.重写toString()方法

这个算是我个人的习惯把,喜欢给自己创建的标准类去重写toStiring()

@Override
    public String toString() {
        return getID();
    }

boolean faceUP

private boolean faceUP;//牌面是否向上用来描述一张牌的状态,表示一张牌是否是正面,也就是有数字的一面。
如果它是false,表示整张牌时背面,看不到,在展示时会展示牌背的图片

3.png

这里我对这个变量的setFaceUP方法(也相当于函数)进行了一些更改:

public void setFaceUP(boolean faceUP) {
        //如果面向一样,不修改
        if (isFaceUP() == faceUP) {
            return;
        }
        //修改方向
        if (faceUP) {
            this.setIcon(new ImageIcon(path + "\\" + ID + ".png"));
        } else {
            this.setIcon(new ImageIcon(path + "\\rear.png"));
        }
        this.faceUP = faceUP;
    }

实质上就是加了更改图片的一些步骤,如果更改了faceUP这个变量的属性,就要同时去改变他所展示的图片。
this.setIcon(new ImageIcon(path + "\\" + ID + ".png"));
这个方法中的this就代表的是一个poker对象,setIcon(new ImageIcon(path + "\\" + ID + ".png"))JLable中继承过来的方法,用于设置(修改)组件所展示的图片。这里的path + "\\" + ID + ".png",就表示的是图片的位置。

path = "DouDiZhu\\image\\poker"
ID就是指这个poker对象的ID,比如如果是黑桃A的话 ID = 1-1
总体链接起来就是"DouDiZhu\\image\\poker \\ 1-1 .png" 也就是图片【♠A】所在的路径。
至于另一个"DouDiZhu\\image\\poker \\rear.png"就是牌的背面所在的路径,可以发现,它是不随这牌对象的变化而变化的,因为牌背只有一个图片。

对于这个boolean faceUP其实我还为他写了一个扩展的方法:

//翻转牌
    public void turn() {
        setFaceUP(!isFaceUP());
    }

这个方法很简单,我当时写的时候以为它很有用,但事实上现在看来没有什么用处,反而还增加了不少的不稳定性,因为我不知道他执行完这个方法后他的牌是正面还是反面(除非我知道他原本的正反)。
但其实它也说不定有些用处,比如你想让所有的牌全部翻面,正面的翻到背面,背面得翻到正面。当然,我不知道为什么要实现这个,也许有一天就要实现这个功能了呢。就算没用,写出来放着就行了,不用就得了。(当然如果方法比较多还是删了的好,避免方法太多)

Point point

private Point point;//牌当前的位置关于这个变量,我只能说不好意思,原本这个变量我是不用定义的。
Point就是点呗, 他是java内java.awt.*下的一个类表示一个二维点,里面其实就两个内容int x; int y;,就是描述一个点的类。用来表示牌在 ‘ 桌子上的位置 ’ ,其实就是在界面上的位置,(因为我使用的是<u>自定义布局</u>,所以需要对每一个组件单独设置位置)

关于这个变量private Point point;其实在牌类Poker继承JLable时就继承了他的this.setLocation();this.getLocation()方法,这两个方法就是设置组件Poker当前位置和获取它当前位置的。
但我现在也不想改,毕竟我的程序已经能够完美运行了,何必还要去为了这点小事破坏了呢,主要是怕一改全出错,当然也可能有我懒的原因。

对于这个变量,我对他的set方法做了一点改动,

public void setPoint(Point toPoint) {
        move(toPoint);
        this.point = toPoint;
    }

这里的move(toPoint)方法有两个功能

  1. 就是显示动画(牌移动的动画)
  2. 就是设置牌组件的位置
//移动牌(动画)
    public void move(Point to) {
        //获取移动起始位置
        Point from = this.point;
        //判断是否需要移动
        if (from.equals(to)) {
            return;
        }
        //获取移动方向
        int mX = to.x < from.x ? -20 : 20;//x轴移动方向
        int mY = to.y < from.y ? -20 : 20;//y轴移动方向
        //移动动画
        while (Math.abs(to.x - from.x) > 20 || Math.abs(to.y - from.y) > 20) {
            if (Math.abs(to.x - from.x) > 20) {
                from.translate(mX, 0);
            }
            if (Math.abs(to.y - from.y) > 20) {
                from.translate(0, mY);
            }
            this.setLocation(from);
            try {
                Thread.sleep(PokerUtil.MOVE_TIME);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //设置最终位置
        this.setLocation(to);
    }

其实所谓的设置最终的位置,实际上就只有最后的一个this.setLocation(to);//设置最终位置,前面的都是动画实现的过程。

所谓动画的实现,想必也应该看出来了,就是把牌一点一点的移动,向着目标每次移动20px(注:px是像素)然后暂停一段时间,接近目标后就直接设置最终位置结束。

Thread.sleep();表示休眠,PokerUtil.MOVE_TIME是我自己的 ‘牌工具类’ 中的一个常量,表示动画间隔暂停的时间,
我设置的PokerUtil.MOVE_TIME=5

from.translate(mX, 0);是Point类中移动点的方法,from是一个点---Point from

等同于把点的值加上括号内的值,该方法的底层实现方法是:

public void translate(int dx, int dy) {
    this.x += dx;
    this.y += dy;
}

boolean top

这个变量也表示牌的一种状态,是否被点击弹起。因为在我们玩牌的时候,会有一个选中牌的操作,牌被点击就会弹起,这个变量就是用来控制这个的。

所以它的set方法也被我做了一点手脚:

//isTop方法不动
public boolean isTop() {
        return top;
    }

    public boolean setTop(boolean top) {
        //如果状态一样,不用修改
        if (isTop() == top) {
            return false;
        }
        Point p = this.getPoint();
        Point newPoint;
        if (top) {
            //弹起
            System.out.println("                                          " + getID() + "->up");
            newPoint = new Point(p.x, p.y - 10);
        } else {
            //落下
            System.out.println("                                          " + getID() + "->down");
            newPoint = new Point(p.x, p.y + 10);
        }
        this.setLocation(newPoint);
        this.setPoint(newPoint);
        this.top = top;
        return true;
    }

和前面的setFaceUP逻辑差不多,都是把变量的切换与状态的改变进行了链接。所谓弹起选中,其实就是把图片向上移动个10px(像素)。

注意:中间的这个,只是用来方便设计时在控制台面板上看的,对程序运行没有任何影响。

System.out.println(" " + getID() + "->up");

boolean canTop

最后一个变量就是用来解决-玩家可以点击牌吗?这个问题的。你不可能说,我点对手的牌或者说是已经打出的牌还能弹起来吧。它就是一个开关,在鼠标监听的时候或用到它进行判断,等下面再进行介绍。

构造方法

对于构造方法,我并没有留下标准的public空参构造 +全参构造,因为你要是没有把基本属性赋值它这个对象本身就是没有意义的,我留下的是一个私有化的构造方法。不允许外面去创建Poker的对象,而是我自己提供了一个获取Poker牌组的方法(代码在下面,暂时先不介绍)。

private Poker(String ID, boolean faceUP, Point localPoint, boolean top, boolean canTop) {
        //设置参数
        this.ID = ID;
        this.top = top;
        this.canTop = canTop;
        
        //添加图片
        //通过翻牌刷新图片
        this.faceUP = !faceUP;
        this.turn();
    
        //统一设置牌位置
        this.point = localPoint;
        this.setBounds(localPoint.x, localPoint.y, 71, 96);
    
        //设置监听事件
        this.addMouseListener(this);
    }

构造方法由四个部分组成:

  1. 设置参数

  2. 添加图片

    这里本来可以用更简单的方法,直接设置图片和faceUP,但我却用了先设置反向,然后立马转正。通过turn()里面的setFaceUP里面的设置图片方法完成初始化,实际上代码可读性差了点,完全可以替换成this.faceUP = faceUP; this.setIcon(new ImageIcon(path + "\\" + ID + ".png"));

  3. 设置位置

    提前把所有牌放在正中间,方便等会发牌。

  4. 设置监听

    设置监听MouseListener通过这个接口可以实现监听鼠标动作,也就是获取鼠标的一些信息。

    实现了这个接口后可以重写五个方法,这里只用到第一个mouseClicked检测鼠标完成一次完整的点击(按下和松开),如果鼠标完成了一次点击就会执行内部的方法。

      @Override
       public void mouseClicked(MouseEvent e) {
           if(e.getButton()==MouseEvent.BUTTON1){
               System.out.println("点击卡牌");
               if (!canTop) {
                   return;
               }
               toTop();
           }
       }
   
       @Override
       public void mousePressed(MouseEvent e) {
   
       }
   
       @Override
       public void mouseReleased(MouseEvent e) {
   
       }
   
       @Override
       public void mouseEntered(MouseEvent e) {
   
       }
   
       @Override
       public void mouseExited(MouseEvent e) {
   
       }

这里可以看到(鼠标点击一次后)执行的方法就是toTop()去改变一次牌的选中弹起状态。
而且在前面还有个限制if (!canTop) {return;}如果不能点击,就不会执行后面的toTop()方法。

最前面的e.getButton()==MouseEvent.BUTTON1是用来判断点击的是不是鼠标左键,如果是就返回true

同理你也可以判断是右键还是中键还是其他的键。。

创建牌的静态方法

下面这个就是创建牌的静态方法,也是唯一获取Poker对象的方法,他会返回一个Poker的集合ArrayList<Poker>相当于“牌盒”

//创建牌(工具)
    public static ArrayList<Poker> newAllPokers() {
        ArrayList<Poker> allPokers = new ArrayList<>();
        //装牌
        for (int i = 1; i <= 4; i++) {
            for (int j = 1; j <= 13; j++) {
                allPokers.add(new Poker(i + "-" + j, false, new Point(360, 220), false, false));
            }
        }
        allPokers.add(new Poker("5-1", false, new Point(360, 220), false, false));
        allPokers.add(new Poker("5-2", false, new Point(360, 220), false, false));
        return allPokers;
    }

原理就是利用了ID名与图片路径的关系,挨个遍历得到结果,最后在把特殊的大小王添加上去。

--------END
估计以后散了,后面直接展示。

一、GameJFrame

package com.itmeiha.game;

import com.itmeiha.domain.MyTools;
import com.itmeiha.domain.Poker;
import com.itmeiha.domain.PokerComputer;
import com.itmeiha.domain.PokerUtil;

import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Random;

public class GameJFrame extends JFrame implements MouseListener {
    String strInfo;//为获得跨函数信息(抢地主的回信)
    int local;//表示当前行动的玩家
    Container contentPane;
    ArrayList<Poker> allPokers;//牌盒,装所有的牌
    ArrayList<ArrayList<Poker>> handPokers = new ArrayList<>();//所有人的手牌
    ArrayList<ArrayList<Poker>> putPokers = new ArrayList<>();//所有人刚打出的牌
    ArrayList<Poker> threePokers = new ArrayList<>();//地主手牌
    JButton threeTrueButton = new JButton();//叫地主·按钮
    JButton threeFalseButton = new JButton(); //不叫地主·按钮
    JButton promptButton = new JButton();//提示·按钮
    JButton fightFalseButton = new JButton();//不要·按钮
    JButton fightTrueButton = new JButton();//出牌·按钮
    JLabel threeSign = new JLabel();//地主标记
    JTextField countTextField = new JTextField();//倒计时框

    public GameJFrame() {
        //设置图标
        setIconImage(Toolkit.getDefaultToolkit().getImage("DouDiZhu\\image\\diZhu.png"));
        //初始化界面
        initJFrame();
        //初始化组件
        initView();
        //将界面显示出来
        this.setVisible(true);
        //开始游戏
        gameStart();
    }

    private void gameStart() {
        Collections.shuffle(allPokers);//洗牌
        Collections.shuffle(allPokers);//洗牌
        Collections.shuffle(allPokers);//洗牌
        //发地主牌
        sendThreePoker();
        //获取一张明牌
        Poker onePoker = takeOnePoker();
        //发牌看牌
        sendPoker();
        lookPoker(0);
        lookPoker(1);
        lookPoker(2);
        //寻找那张明牌(第一个地主)
        int oneThree = findOneThree(onePoker);
        System.out.println("开始玩家:" + oneThree);
        //选择地主的操作
        int tureThree = findTureThree(oneThree);
        if (tureThree == -1) {
            //重开
            this.setVisible(false);
            new GameJFrame();
            return;
        }
        System.out.println("地主:" + tureThree);
        //地主拿牌
        takeThreePoker(tureThree);
        strInfo = "";//清空原有信息
        local = tureThree;//真地主开始
        while (true) {
            System.out.println("--------------------------------------");
            System.out.println("local: " + local + "; strInfo:" + strInfo);
            if (local == 1) {
                //玩家行动
                PokerUtil.setTopPoker(handPokers.get(1), false);//取消选中
                playerStartPoker();
            } else {
                //电脑行动
                PokerComputer.countdown(local, countTextField, PokerUtil.PUT_WAIT_TIME);//电脑的倒计时
                boolean b = PokerComputer.startPoker(handPokers, local, strInfo, tureThree);
                if (b) {
                    //可以出
                    boolean b1 = localPutTopPokersToDesk();
                    if (!b1) {
                        System.out.println("电脑出牌规则错误");
                    }
                } else {
                    //如果没法出牌
                    localClosePokers();
                }
            }

            //判断是否胜利
            if (handPokers.get(local).size() == 0) {
                //胜利
                if (local == 1) {
                    strInfo = "玩家1 获胜";
                } else {
                    strInfo = "电脑" + local + " 获胜";
                }
                MyTools.showJDialog(strInfo);

                //打开所有人的牌
                for (ArrayList<Poker> handPoker : handPokers) {
                    for (Poker poker : handPoker) {
                        poker.setCanTop(false);
                        poker.setTop(false);
                        poker.setFaceUP(true);
                    }
                }
                break;
            }

            lookPoker(local);//当前玩家理牌
            nextLocal();//下一个玩家
            clearDeskFor(local);//把下家的牌清空
        }
    }

    private void playerStartPoker() {
        //记录初始信息
        String tempInfo = strInfo;
        //显示按钮
        promptButton.setVisible(true);
        fightTrueButton.setVisible(true);
        if (!strInfo.equals("")) {
            fightFalseButton.setVisible(true);
        }
        //等待
        //设置倒计时位置
        countTextField.setLocation(360, 380);
        //显示倒计时
        countTextField.setVisible(true);
        //计时等待
        for (int i = PokerUtil.PUT_WAIT_TIME; i > 0; i--) {
            countTextField.setText("倒计时:" + i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //出完牌,退出计时
            if (!strInfo.equals(tempInfo)) {
                break;
            }
        }
        //隐藏按钮
        fightFalseButton.setVisible(false);
        promptButton.setVisible(false);
        fightTrueButton.setVisible(false);
        if (tempInfo.equals(strInfo)) {
            //超时
            //弹出提醒
            countTextField.setText("超时!");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (strInfo.equals("") || PokerUtil.AUTO_PLAYER) {
                //电脑代替出牌
                boolean b = PokerComputer.startPoker(handPokers, local, strInfo, -1);
                if (b) {
                    //可以出
                    if (!localPutTopPokersToDesk()) {
                        System.out.println("电脑“替代玩家”出牌时规则错误");
                    } else if (!PokerUtil.AUTO_PLAYER) {
                        //如果没法出牌
                        System.out.println("出错,null时,玩家不出牌");
                    } else {
                        //自动时默认不出
                        localClosePokers();
                    }
                } else {
                    //默认直接不出
                    localClosePokers();
                }
            }
        }
        countTextField.setVisible(false);
    }

    private void takeThreePoker(int id) {
        Poker[] pokers = new Poker[3];
        //拿地主牌
        for (int i = 0; i < 3; i++) {
            Poker poker = threePokers.get(0);
            //暂时记录poker,方便展示动画
            pokers[i] = poker;
            if (id == 1) {
                poker.turn();
            }
            handPokers.get(id).add(threePokers.remove(0));
        }
        lookPoker(id);

        //只有是玩家才进行额外操作
        if (id == 1) {
            PokerUtil.allCanTop(pokers, false);
            for (Poker poker : pokers) {
                poker.setTop(true);
            }
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            for (Poker poker : pokers) {
                poker.setTop(false);
            }
            PokerUtil.allCanTop(pokers, true);
        }

    }

    private int findTureThree(int oneThree) {
        local = oneThree;
        //判断谁进行了抢地主操作
        strInfo = "";
        for (int i = 3; i > 0; i--) {
            whoIsThree(local);
            nextLocal();
        }
        //判断地主情况
        int tureThree;//真正的地主
        if (strInfo.length() == 0) {
            //重发牌,重开游戏
            return -1;
        } else if (strInfo.length() == 1) {
            //抢的那一个是地主
            tureThree = Integer.parseInt(strInfo);
        } else if (strInfo.length() == 2) {
            int threes = Integer.parseInt(strInfo);
            int three2 = threes % 10;
            int three1 = threes / 10;
            if (whoIsThree(three1)) {
                //问第一个人要不要
                tureThree = three1;
            } else {
                //直接给第二个人
                tureThree = three2;
            }
        } else {
            int threes = Integer.parseInt(strInfo);
            int three3 = threes % 10;
            int three2 = threes / 10 % 10;
            int three1 = threes / 100;
            if (whoIsThree(three1)) {
                //问第一个人要不要
                tureThree = three1;
            } else if (whoIsThree(three2)) {
                //问第二个人要不要
                tureThree = three2;
            } else {
                //直接给第三个人
                tureThree = three3;
            }
        }
        //放地主标记
        if (tureThree == 0) {
            threeSign.setLocation(360 - 290, 220 - 200);
        } else if (tureThree == 1) {
            threeSign.setLocation(360 - 250, 420 + 20);
        } else {
            threeSign.setLocation(360 + 310, 220 - 200);
        }
        //展示地主标记
        threeSign.setVisible(true);
        return tureThree;
    }

    private boolean whoIsThree(int id) {
        boolean result = false;
        if (id == 1) {
            //玩家1操作
            System.out.println("等待玩家操作中");
            result = playerIsThree();
        } else if (id == 0) {
            //电脑0操作
            result = PokerComputer.isThree(0, countTextField);
            if (result) {
                strInfo += 0;
            }
        } else if (id == 2) {
            //电脑2操作
            result = PokerComputer.isThree(2, countTextField);
            if (result) {
                strInfo += 2;
            }
        }
        System.out.println("strInfo:" + strInfo);
        return result;
    }

    private boolean playerIsThree() {
        boolean result = false;
        String tempStrInfo = strInfo;//记录当前信息
        //玩家->显示按钮
        threeTrueButton.setVisible(true);
        threeFalseButton.setVisible(true);
        //设置倒计时位置
        countTextField.setLocation(360, 380);
        //显示倒计时
        countTextField.setVisible(true);
        //计时等待
        for (int i = PokerUtil.THREE_WAIT_TIME; i > 0; i--) {
            countTextField.setText("倒计时:" + i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (!tempStrInfo.equals(strInfo)) {
                //做出选择,退出计时
                if (strInfo != null) {
                    //做出了要的选择
                    countTextField.setText("抢地主");
                    result = true;
                } else {
                    //做出了不要的选择
                    countTextField.setText("不抢");
                }
                break;
            }
        }
        //超时
        if (tempStrInfo.equals(strInfo)) {
            //关闭按钮
            threeTrueButton.setVisible(false);
            threeFalseButton.setVisible(false);
            if (!PokerUtil.AUTO_PLAYER) {
                //超时按不抢处理
                System.out.println("超时,不抢");
                countTextField.setText("超时,不要地主");
            } else {
                //开启自动,随机
                Random r = new Random();
                if (r.nextInt(2) == 1) {
                    strInfo += 1;
                } else {
                    strInfo = null;
                }
            }
        }
        //若不抢地主还原str
        if (strInfo == null) {
            strInfo = tempStrInfo;
        }

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        //关闭倒计时
        countTextField.setVisible(false);
        return result;
    }

    private int findOneThree(Poker onePoker) {
        int id = -1;
        //寻找第一张明牌
        for (int i = 0; i < 3; i++) {
            if (handPokers.get(i).contains(onePoker)) {
                id = i;
            }
        }
        return id;
    }

    private Poker takeOnePoker() {
        //随机抽取一张牌
        Random r = new Random();
        Poker onePoker = allPokers.get(r.nextInt(allPokers.size()));
        //拿出这张牌看
        Point p = onePoker.getPoint();
        onePoker.setPoint(p.x + 100, p.y);
        onePoker.turn();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //放回去
        onePoker.setPoint(p.x - 100, p.y);
        onePoker.turn();
        //放在牌堆里面,并记录这张牌
        contentPane.setComponentZOrder(onePoker, allPokers.size());
        return onePoker;
    }

    private void lookPoker(int i) {
        PokerUtil.sortPoker(handPokers.get(i));
        int n = 0;//牌的偏差
        if (i == 0) {
            //整理牌0
            for (Poker poker : handPokers.get(0)) {
                poker.setPoint(360 - 300, 220 - 150 + 15 * n++);
                contentPane.setComponentZOrder(poker, 0);
            }
        } else if (i == 1) {
            //整理牌1
            for (Poker poker : handPokers.get(1)) {
                poker.setPoint(210 + 20 * n++, 420);
                poker.setCanTop(true);//让牌可以被点击
                contentPane.setComponentZOrder(poker, 0);
            }
        } else {
            //整理牌2
            for (Poker poker : handPokers.get(2)) {
                poker.setPoint(360 + 300, 220 - 150 + 15 * n++);
                contentPane.setComponentZOrder(poker, 0);
            }
        }
    }

    private void sendThreePoker() {
        int d = 0;//地主牌的偏差
        for (int i = 0; i < 3; i++) {
            //获取牌顶的牌
            Poker poker = allPokers.get(0);
            //作为地主牌
            poker.setPoint(285 + 75 * d++, 20);
            //翻面
            /*poker.turn();*/
            //放到地主集合中
            threePokers.add(allPokers.remove(0));
        }
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private void sendPoker() {
        //发牌
        int a = 0;//左边牌的偏差
        int b = 0;//自己牌的偏差
        int c = 0;//右边牌的偏差
        //确保循环allPoker.size不发生变化
        int size = allPokers.size();
        for (int i = 0; i < size; i++) {
            Poker poker = allPokers.get(0);
            if (i % 3 == 0) {
                //左边的牌
                poker.setPoint(360 - 300, 220 - 150 + 15 * a++);
            } else if (i % 3 == 1) {
                //自己的牌
                poker.setPoint(210 + 20 * b++, 420);
                poker.turn();
            } else {
                //右边的牌
                poker.setPoint(360 + 300, 220 - 150 + 15 * c++);
            }
            //放入对应手牌管理
            handPokers.get(i % 3).add(allPokers.remove(0));
            //把当前的牌至于最顶端,这样就会有牌依次错开且叠起来的效果
            contentPane.setComponentZOrder(poker, 0);
        }


    }

    private void initView() {
        allPokers = Poker.newAllPokers();//拿一副新牌
        //显示牌盒里的牌
        System.out.println("allPokers:" + allPokers);
        for (Poker poker : allPokers) {
            this.add(poker);
        }

        //每个人的手牌
        //交给handPokers管理,0左,1我,2右
        handPokers.add(new ArrayList<>());
        handPokers.add(new ArrayList<>());
        handPokers.add(new ArrayList<>());

        //每个人打出的牌
        //交给putPokers管理,0左,1我,2右
        putPokers.add(new ArrayList<>());
        putPokers.add(new ArrayList<>());
        putPokers.add(new ArrayList<>());

        //显示按钮
        //叫地主·按钮
        threeTrueButton.setBounds(360 + 75, 365, 120, 50);//设置位置大小
        threeTrueButton.setText("叫地主");//设置文本
        threeTrueButton.setFont(new Font(null, Font.BOLD, 20));//设置字体
        threeTrueButton.setForeground(Color.BLACK);//文字颜色
        threeTrueButton.setContentAreaFilled(false);
        threeTrueButton.setBorderPainted(false);//删除按钮边框
        threeTrueButton.addMouseListener(this);
        threeTrueButton.setVisible(false);//隐藏
        contentPane.add(threeTrueButton);
        //不叫地主·按钮
        threeFalseButton.setBounds(360 - 100, 365, 90, 50);
        threeFalseButton.setText("不叫");//设置文本
        threeFalseButton.setFont(new Font(null, Font.BOLD, 20));//设置字体
        threeFalseButton.setForeground(Color.BLACK);//文字颜色
        threeFalseButton.setContentAreaFilled(false);
        threeFalseButton.setBorderPainted(false);//删除按钮边框
        threeFalseButton.addMouseListener(this);
        threeFalseButton.setVisible(false);//隐藏
        contentPane.add(threeFalseButton);
        //提示·按钮
        promptButton.setBounds(360 + 60, 365, 90, 50);//设置位置大小
        promptButton.setText("提示");//设置文本
        promptButton.setFont(new Font(null, Font.BOLD, 16));//设置字体
        promptButton.setForeground(Color.BLACK);//文字颜色
        promptButton.setContentAreaFilled(false);
        promptButton.setBorderPainted(false);//删除按钮边框
        promptButton.addMouseListener(this);
        promptButton.setVisible(false);//隐藏
        contentPane.add(promptButton);
        //不要·按钮
        fightFalseButton.setBounds(360 - 150, 365, 90, 50);
        fightFalseButton.setText("不要");//设置文本
        fightFalseButton.setFont(new Font(null, Font.BOLD, 20));//设置字体
        fightFalseButton.setForeground(Color.BLACK);//文字颜色
        fightFalseButton.setContentAreaFilled(false);
        fightFalseButton.setBorderPainted(false);//删除按钮边框
        fightFalseButton.addMouseListener(this);
        fightFalseButton.setVisible(false);//隐藏
        contentPane.add(fightFalseButton);
        //出牌·按钮
        fightTrueButton.setBounds(360 + 150, 365, 90, 50);//设置位置大小
        fightTrueButton.setText("出牌");//设置文本
        fightTrueButton.setFont(new Font(null, Font.BOLD, 20));//设置字体
        fightTrueButton.setForeground(Color.BLACK);//文字颜色
        fightTrueButton.setContentAreaFilled(false);
        fightTrueButton.setBorderPainted(false);//删除按钮边框
        fightTrueButton.addMouseListener(this);
        fightTrueButton.setVisible(false);//隐藏
        contentPane.add(fightTrueButton);

        //设置地主标记
        threeSign.setIcon(new ImageIcon("DouDiZhu\\image\\diZhu.png"));//设置图片
        threeSign.setSize(40, 40);//设置大小
        threeSign.setVisible(false);//隐藏
        contentPane.add(threeSign);

        //设置倒计时框
        countTextField.setSize(80, 25);
        countTextField.setEditable(false);
        countTextField.setVisible(false);
        contentPane.add(countTextField);
    }

    private void initJFrame() {
        this.setTitle("斗地主");
        this.setSize(830, 620);
        this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        this.setResizable(false);//设置窗口无法调节
        this.setLocationRelativeTo(null);
        contentPane = this.getContentPane();//直接获取隐藏容器
        contentPane.setLayout(null);//取消居中
        contentPane.setBackground(Color.GRAY);//设置背景色
    }

    private void nextLocal() {
        local = getNextLocal();//下一个
    }

    private int getNextLocal() {
        return local == 2 ? 0 : local + 1;//下一个
    }

    @Override
    public void mouseClicked(MouseEvent e) {

    }

    @Override
    public void mousePressed(MouseEvent e) {

    }

    @Override
    public void mouseReleased(MouseEvent e) {
        Object obj = e.getSource();
        if (obj == threeTrueButton) {
            System.out.println("要地主");
            //删除按钮
            threeFalseButton.setVisible(false);
            threeTrueButton.setVisible(false);
            //已做选择要,更改权重值
            strInfo += 1;
        } else if (obj == threeFalseButton) {
            System.out.println("不要地主");
            //删除按钮
            threeTrueButton.setVisible(false);
            threeFalseButton.setVisible(false);
            //已做选择不要
            strInfo = null;//传递信息,不要
        } else if (obj == promptButton) {
            System.out.println("提示");
            PokerUtil.setTopPoker(handPokers.get(local), false);
            PokerComputer.startPoker(handPokers, local, strInfo, -1);
        } else if (obj == fightTrueButton) {
            System.out.println("出牌");
            //尝试出牌,如果不符要求则不进行操作
            boolean b = localPutTopPokersToDesk();
            if (!b) {
                System.out.println("id:" + local + ",出牌不合规");
            }
        } else if (obj == fightFalseButton) {
            System.out.println("不要");
            PokerUtil.setTopPoker(handPokers.get(local), false);
            localClosePokers();
        }
    }

    //尝试把当前“出牌者”选择的牌放在桌子上,如果不符合要求则不进行操作
    private boolean localPutTopPokersToDesk() {
        //0.把牌先拿出来看
        ArrayList<Poker> topPoker = PokerUtil.getTopPoker(handPokers.get(local), false);
        //1.计算牌组的价值
        String valuedOfPokers = PokerUtil.valueOfPokers(topPoker);
        //2.是否符合规则//3.是否允许在此时打出(和上家类型是否相同)
        if (valuedOfPokers != null && !valuedOfPokers.equals("") && PokerUtil.canAttack(valuedOfPokers, strInfo)) {
            //符合规则
            //4.把牌组放在桌子上
            PokerUtil.getTopPoker(handPokers.get(local), true);//确认删除
            putPokerToDesk(topPoker);
            //出牌完毕
            strInfo = valuedOfPokers;//传递牌组的价值
        } else {
            String warning = "canAttack->false";
            if (valuedOfPokers == null) {
                warning = " valuedOfPokers = true NULL";
            } else if (valuedOfPokers.equals("")) {
                warning = " valuedOfPokers = \"\"";
            }
            System.out.println("localPutTopPokersToDesk:false, " + warning);
            return false;
        }
        return true;
    }

    //不出牌
    public void localClosePokers() {
        System.out.println("id:" + local + ",不出牌");
        countTextField.setText("不出");
        countTextField.setVisible(true);
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        countTextField.setVisible(false);

        //直接修改出牌信息
        if (strInfo.endsWith("X")) {
            strInfo = "";
        } else {
            strInfo += "&X";
        }
    }

    private void putPokerToDesk(ArrayList<Poker> topPoker) {
        ArrayList<Poker> localPutPokers = putPokers.get(local);
        //把新的牌放上去
        localPutPokers.addAll(topPoker);
        int f = 0;//偏差
        for (Poker poker : localPutPokers) {
            if (local == 0) {
                poker.setPoint(330 - 100, 140 + 15 * f++);
            } else if (local == 2) {
                poker.setPoint(330 + 150, 140 + 15 * f++);
            } else {
                poker.setPoint(350 - 30 + 15 * f++, 250);
            }
            poker.setFaceUP(true);
            poker.setCanTop(false);
            poker.setTop(false);
            contentPane.setComponentZOrder(poker, 0);
        }
    }

    private void clearDeskFor(int lastLocal) {
        ArrayList<Poker> lastLocalPutPokers = putPokers.get(lastLocal);
        int size = lastLocalPutPokers.size();
        for (int i = 0; i < size; i++) {
            Poker poker = lastLocalPutPokers.remove(0);
            poker.setVisible(false);
            allPokers.add(poker);
        }
    }

    @Override
    public void mouseEntered(MouseEvent e) {

    }

    @Override
    public void mouseExited(MouseEvent e) {

    }
}


二、Poker

package com.itmeiha.domain;

import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;

public class Poker extends JLabel implements MouseListener {
    static private final String path = "DouDiZhu\\image\\poker";//牌对应的图片的文件夹地址
    private String ID;//牌名字
    private boolean faceUP;//牌面是否向上
    private Point point;//牌当前的位置
    private boolean top;//牌是否弹起
    private boolean canTop;//牌是否能弹起


    private Poker(String ID, boolean faceUP, Point localPoint, boolean top, boolean canTop) {
        //设置参数
        this.ID = ID;
        this.top = top;
        this.canTop = canTop;
        //添加图片
        //通过翻牌刷新图片
        this.faceUP = !faceUP;
        this.turn();
        //统一设置牌位置
        this.point = localPoint;
        this.setBounds(localPoint.x, localPoint.y, 71, 96);
        //设置监听事件
        this.addMouseListener(this);
    }

    //创建牌(工具)
    public static ArrayList<Poker> newAllPokers() {
        ArrayList<Poker> allPokers = new ArrayList<>();
        //装牌
        for (int i = 1; i <= 4; i++) {
            for (int j = 1; j <= 13; j++) {
                allPokers.add(new Poker(i + "-" + j, false, new Point(360, 220), false, false));
            }
        }
        allPokers.add(new Poker("5-1", false, new Point(360, 220), false, false));
        allPokers.add(new Poker("5-2", false, new Point(360, 220), false, false));
        return allPokers;
    }

    //移动牌(动画)
    public void move(Point to) {
        //获取移动起始位置
        Point from = this.point;
        //判断是否需要移动
        if (from.equals(to)) {
            return;
        }
        //获取移动方向
        int mX = to.x < from.x ? -20 : 20;//x轴移动方向
        int mY = to.y < from.y ? -20 : 20;//y轴移动方向
        //移动动画
        while (Math.abs(to.x - from.x) > 20 || Math.abs(to.y - from.y) > 20) {
            if (Math.abs(to.x - from.x) > 20) {
                from.translate(mX, 0);
            }
            if (Math.abs(to.y - from.y) > 20) {
                from.translate(0, mY);
            }
            this.setLocation(from);
            try {
                Thread.sleep(PokerUtil.MOVE_TIME);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //设置最终位置
        this.setLocation(to);
    }

    //翻转牌
    public void turn() {
        setFaceUP(!isFaceUP());
    }

    //弹起或落下
    public void toTop() {
        setTop(!isTop());
    }

    public int getNumber() {
        return Integer.parseInt(getID().substring(2));
    }

    public String getID() {
        return ID;
    }

    public void setID(String ID) {
        this.ID = ID;
    }

    public boolean isFaceUP() {
        return faceUP;
    }

    public void setFaceUP(boolean faceUP) {
        //如果面向一样,不修改
        if (isFaceUP() == faceUP) {
            return;
        }
        if (faceUP) {
            this.setIcon(new ImageIcon(path + "\\" + ID + ".png"));
        } else {
            this.setIcon(new ImageIcon(path + "\\rear.png"));
        }
        this.faceUP = faceUP;
    }

    public Point getPoint() {
        return point;
    }


    public void setPoint(int toX, int toY) {
        setPoint(new Point(toX, toY));
    }

    public void setPoint(Point toPoint) {
        move(toPoint);
        this.point = toPoint;
    }

    public boolean isTop() {
        return top;
    }

    public boolean setTop(boolean top) {
        //如果状态一样,不用修改
        if (isTop() == top) {
            return false;
        }
        Point p = this.getPoint();
        Point newPoint;
        if (top) {
            //弹起
            System.out.println("                                          " + getID() + "->up");
            newPoint = new Point(p.x, p.y - 10);
        } else {
            //落下
            System.out.println("                                          " + getID() + "->down");
            newPoint = new Point(p.x, p.y + 10);
        }
        this.setLocation(newPoint);
        this.setPoint(newPoint);
        this.top = top;
        return true;
    }

    public boolean isCanTop() {
        return canTop;
    }

    public void setCanTop(boolean canTop) {
        this.canTop = canTop;
    }

    @Override
    public String toString() {
        return getID();
    }

    @Override
    public void mouseClicked(MouseEvent e) {
        if(e.getButton()==MouseEvent.BUTTON1){
            System.out.println("点击卡牌");
            if (!canTop) {
                return;
            }
            toTop();
        }
    }

    @Override
    public void mousePressed(MouseEvent e) {

    }

    @Override
    public void mouseReleased(MouseEvent e) {

    }

    @Override
    public void mouseEntered(MouseEvent e) {

    }

    @Override
    public void mouseExited(MouseEvent e) {

    }
}


三、PokerUtil

package com.itmeiha.domain;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Set;

public class PokerUtil {
    public static long MOVE_TIME = 5;
    public static int THREE_WAIT_TIME = 15;
    public static int PUT_WAIT_TIME = 30;
    public static int COMPUTER_RANDOM_TIME = 3;
    public static boolean AUTO_PLAYER = false;

    //是否调整为自动游戏
    public static final boolean AUTO_GAMES = false;

    static {
        if (AUTO_GAMES) {
            MOVE_TIME = 0;
            THREE_WAIT_TIME = 0;
            PUT_WAIT_TIME = 0;
            AUTO_PLAYER = true;
        }
    }
    //判断是否能出牌

    public static boolean canAttack(String newPokers, String oldPokers) {
        if (oldPokers.equals("")) {
            return true;
        }
        String[] oldSplit = oldPokers.split("&");
        String[] newSplit = newPokers.split("&");

        boolean b;
        //新老类型相同
        if (oldSplit[0].equals(newSplit[0])) {
            b = Integer.parseInt(newSplit[1]) > Integer.parseInt(oldSplit[1]);
        } else {
            //类型不同,新的如果不是炸弹直接false
            b = newSplit[0].equals("4");//新的是不是炸弹
        }
        System.out.println("canPut:" + b);
        return b;
    }

    private static final char[] intToChar = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0' + 10, '0' + 11, '0' + 12, '0' + 13, '0' + 14, '0' + 15, '0' + 16};

    //计算牌组的价值字符串(工具)
    /*1表示单张牌;2表示对子;3表示三张牌;4表示炸弹;5没有;6表示顺子;7表示三顺带牌*/
    /*31表示三带一;32表示三带二;41表示四带对一;42表示四带对二;43表示四带对三*/
    /*‘&以前’表示牌的类型,4为万能类型;‘&后’表示牌组的价值。;*/
    //其中超出9的用十六进制abc表示(只有普通顺子使用)------>改为用9后面的字符值
    public static String valueOfPokers(ArrayList<Poker> pokers) {
        //保证有牌被选中
        if (pokers.size() == 0) {
            return "";
        }
        //判断单出一张王
        if (pokers.size() == 1 && pokers.get(0).toString().startsWith("5")) {
            return "1&2" + pokers.get(0).getNumber();
        }
        //判断王炸的可能
        if (pokers.size() == 2 && pokers.get(0).toString().startsWith("5") && pokers.get(1).toString().startsWith("5")) {
            System.out.println("王炸");
            return "4&20";
        }
        //确保牌组内无王
        for (Poker poker : pokers) {
            if (poker.toString().startsWith("5")) {
                return null;
            }
        }

        //获取牌组内对应数字及其个数
        HashMap<Integer, Integer> pokerCountMap = getPokerCountMap(pokers);
        Set<Integer> keyedSet = pokerCountMap.keySet();

        //判断N个相同数字的可能
        for (Integer key : keyedSet) {
            if (pokerCountMap.get(key) == pokers.size()) {
                return pokers.size() + "&" + key;
            }
        }

        //判断N个数字顺子的可能
        {
            //找到顺子的开始
            int start;
            for (start = 3; start <= 15; start++) {
                if (keyedSet.contains(start)) {
                    break;
                }
            }
            int index = start;
            //记录顺子的组数
            int n = 1;
            //记录顺子的元素个数
            int count = pokerCountMap.get(index);
            while (index <= 15) {
                //判断是否又连续的后一张牌
                if (keyedSet.contains(++index)) {
                    //有
                    //判断是不是和开头的牌数一样
                    if (pokerCountMap.get(index) == count) {
                        //是,继续回去,判断
                        n++;
                    } else {
                        //不是,不是顺子
                        n = 0;
                        break;
                    }
                } else {
                    //没有
                    //判断后面是否还有牌
                    while (index <= 15) {
                        if (keyedSet.contains(++index)) {
                            //有牌,不是顺子
                            n = 0;
                            break;
                        }
                    }
                    //没牌,可能是顺子
                    break;
                }
            }
            if (n * count >= 5) {
                return "6" + intToChar[n] + count + "&" + start;
            }
        }

        //统计单张牌a1和对子b2和三个c3和“炸弹”d4的数量
        int a1 = 0;
        int b2 = 0;
        int c3 = 0;
        int d4 = 0;
        for (int key : keyedSet) {
            int value = pokerCountMap.get(key);
            if (value == 1) {
                a1++;
            } else if (value == 2) {
                b2++;
            } else if (value == 3) {
                c3++;
            } else if (value == 4) {
                d4++;
            }
        }
        //"炸弹"只能有一次
        if (d4 > 1) {
            return null;
        }
        //判断4带2可能
        if (d4 == 1 && (a1 == 2 || b2 == 2 || c3 == 2) && a1 + b2 + c3 == 2) {
            String str = "4";
            if (a1 == 2) {
                str += 1;
            } else if (b2 == 2) {
                str += 2;
            } else {
                str += 3;
            }
            int lastKey = 0;
            for (int key : keyedSet) {
                if (pokerCountMap.get(key) == 4) {
                    lastKey = key;
                }
            }
            return str + "&" + lastKey;
        }

        //判断是否能形成3带
        if (c3 == 0) {
            return null;
        }
        //判断3带1可能
        //1.判断连续性
        //获取所有的 三个相同 数字
        int[] c3s = new int[c3];
        int n = 0;
        for (int key : keyedSet) {
            if (pokerCountMap.get(key) == 3) {
                c3s[n++] = key;
            }
        }
        //从小到大排序
        Arrays.sort(c3s);
        //若这些数字不连续,直接null
        int first = c3s[0];
        for (int i = 0; i < c3s.length; i++) {
            if (first + i != c3s[i]) {
                return null;
            }
        }
        //判断多种情况
        if ((a1 == c3 || b2 == c3) && a1 + b2 == c3) {
            //剩余的单张(或对子)与 三个 的数量一致
            String str = "";
            if (c3 == 1) {
                //一个 三带 的情况
                str += 3;
            } else {
                //多个 三带 的情况
                str = str + 7 + c3;
            }
            //判断带的是一张还是一对
            if (a1 == c3) {
                str += 1;
            } else {
                str += 2;
            }
            return str + "&" + first;
        }
        return null;
    }

    //得到牌组所对应的所有数字,除了大小王
    public static HashMap<Integer, Integer> getPokerCountMap(ArrayList<Poker> pokers) {
        //获取数字
        Integer[] integers = new Integer[pokers.size()];
        for (int i = 0; i < pokers.size(); i++) {
            String str = pokers.get(i).toString();
            if (str.startsWith("5")) {
                integers[i] = -1;
                continue;
            }
            int number = Integer.parseInt(str.substring(2));
            //把A和2升值
            int value = number < 3 ? number + 13 : number;
            integers[i] = value;
        }
        //统计个数
        return MyTools.countInt(integers);
    }

    //获得所有被选中的牌(工具)
    public static ArrayList<Poker> getTopPoker(ArrayList<Poker> pokers, boolean isRemove) {
        ArrayList<Poker> newPokers = new ArrayList<>();
        for (Poker poker : pokers) {
            if (poker.isTop()) {
                newPokers.add(poker);
            }
        }
        if (isRemove) {
            for (Poker poker : newPokers) {
                poker.setCanTop(false);
                poker.setTop(false);
                pokers.remove(poker);
            }
        }
        return newPokers;
    }

    //设置所有此数字牌为选中状态
    public static int useValueSetTopPoker(ArrayList<Poker> pokers, int value, int count) {
        int number = value > 13 ? value - 13 : value;//把价值恢复成数字
        int b = setTopPoker(pokers, number, count);
        System.out.println("setTopPoker: " + b);
        return b;
    }

    public static int setTopPoker(ArrayList<Poker> pokers, int number, int count) {
        //想要选择4张以上的牌,返回false
        if (count > 4) {
            return -1;
        }
        //拿出要选中的牌
        //判断是选中还是取消
        boolean isTop = count >= 0;
        //获取数量
        count = Math.abs(count);

        Poker[] tempPoker = new Poker[count];
        int i = 0;
        for (Poker poker : pokers) {
            //不算王牌
            if (poker.getID().startsWith("5")) {
                continue;
            }
            //寻找数字
            if (poker.getNumber() == number) {
                poker.setTop(true);
                tempPoker[i++] = poker;
                //判断是否符合要求
                if (i == count) {
                    //符合要求,选中,退出
                    for (Poker poker1 : tempPoker) {
                        boolean b = poker1.setTop(isTop);
                        if (!b) {
                            //卡牌已被选择返回0
                            return 0;
                        }
                    }
                    System.out.println("setTopPoker:" + Arrays.toString(tempPoker));
                    return 1;
                }
            }
        }
        //不符合要求,直接退出
        return -1;
    }

    //排序牌(工具)
    public static void sortPoker(ArrayList<Poker> pokerArrayList) {
        pokerArrayList.sort((Poker o1, Poker o2) -> {
            String[] split1 = o1.toString().split("-");
            String[] split2 = o2.toString().split("-");
            int color1 = Integer.parseInt(split1[0]);
            int color2 = Integer.parseInt(split2[0]);
            int number1 = Integer.parseInt(split1[1]);
            int number2 = Integer.parseInt(split2[1]);
            if (color1 == 5) {
                number1 += 100;
            } else if (number1 <= 2) {
                number1 += 20;
            }
            if (color2 == 5) {
                number2 += 100;
            } else if (number2 <= 2) {
                number2 += 20;
            }
            return number1 != number2 ? number2 - number1 : color2 - color1;
        });
    }

    //让所有牌能或者不能点击
    public static void allCanTop(Poker[] pokers, boolean canTop) {
        for (Poker poker : pokers) {
            poker.setCanTop(canTop);
        }
    }

    public static void setTopPoker(ArrayList<Poker> pokers, boolean top) {
        for (Poker poker : pokers) {
            poker.setTop(top);
        }
    }
}


四、MyTools

package com.itmeiha.domain;

import javax.swing.*;
import java.awt.*;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Random;

public class MyTools {
    //统计字符数,只统计非负数
    public static HashMap<Integer, Integer> countInt(Integer[] integers) {
        HashMap<Integer, Integer> map = new HashMap<>();
        for (int n : integers) {
            if (n < 0) {
                continue;
            }
            if (map.containsKey(n)) {
                int temp = map.get(n) + 1;
                map.put(n, temp);
            } else {
                map.put(n, 1);
            }
        }
        return map;
    }

    public static String getCodeStr() {
        StringBuilder sb = new StringBuilder();
        Random r = new Random();

        //随机4字母
        for (int i = 0; i < 4; i++) {
            int n = r.nextInt(26 + 26);
            if (n < 26) {
                sb.append((char) (n + 'A'));
            } else {
                sb.append((char) (n - 26 + 'a'));
            }
        }

        //随机1数字
        sb.append(r.nextInt(10));

        String codeStr = sb.toString();
        System.out.println("codeStr:" + codeStr);
        return codeStr;
    }

    public static void showJDialog(String text) {
        JDialog jDialog = new JDialog();
        jDialog.setSize(200, 150);
        jDialog.setLocationRelativeTo(null);
        jDialog.setAlwaysOnTop(true);
        jDialog.setModal(true);

        jDialog.setTitle("提示");

        JLabel jLabel = new JLabel(text);
        jLabel.setBounds(0, 0, 200, 150);
        jLabel.setFont(new Font(null, Font.BOLD, 15));
        jLabel.setForeground(Color.red);

        jDialog.getContentPane().add(jLabel);

        jDialog.setVisible(true);
    }

    public static ArrayList<String> ReadFrom(String path) {
        ArrayList<String> list = new ArrayList<>();
        try {
            BufferedReader br = new BufferedReader(new FileReader(path));
            String str;
            while ((str = br.readLine()) != null) {
                list.add(str);
            }
            br.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return list;
    }
}


五、Computer 可能有问题,而且实现就是考虑了每一种情况。

package com.itmeiha.domain;

import javax.swing.*;
import java.util.*;

public class PokerComputer {
    static ArrayList<ArrayList<Integer>> countList = new ArrayList<>();

    static {
        countList.add(null);
        countList.add(new ArrayList<>());//1单张
        countList.add(new ArrayList<>());//2对子
        countList.add(new ArrayList<>());//3三个
        countList.add(new ArrayList<>());//4炸弹
    }

    //电脑的倒计时
    public static void countdown(int local, JTextField countTextField, int time) {
        //设置倒计时位置
        if (local == 0) {
            countTextField.setLocation(130, 85);
        } else {
            countTextField.setLocation(720 - 150, 85);
        }
        //显示倒计时
        countTextField.setVisible(true);
        Random r = new Random();
        int r1 = r.nextInt(PokerUtil.COMPUTER_RANDOM_TIME) + 1;//随机思考时间,模拟真人
        //计时等待
        for (int i = time; i > 0; i--) {
            countTextField.setText("倒计时:" + i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (time - i == r1) {
                break;
            }
        }
        countTextField.setVisible(false);
    }

    public static boolean isThree(int three, JTextField countTextField) {
        countdown(three, countTextField, PokerUtil.THREE_WAIT_TIME);//电脑的倒计时
        Random r = new Random();
        boolean result = r.nextInt(2) == 1;//随机结果
        countTextField.setVisible(true);
        if (result) {
            countTextField.setText("抢地主");
        } else {
            countTextField.setText("不抢");
        }
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //隐藏倒计时
        countTextField.setVisible(false);

        return result;
    }

    public static boolean startPoker(ArrayList<ArrayList<Poker>> handPokers, int local, String strInfo, int trueThree) {
        //对自己手上牌进行分析
        ArrayList<Poker> pokers = handPokers.get(local);

        HashMap<Integer, Integer> pokerCountMap = PokerUtil.getPokerCountMap(pokers);//此处开始不考虑王
        System.out.println("pokerCountMap: " + pokerCountMap);
        Set<Integer> keyedSet = pokerCountMap.keySet();

        //统计单子,对子,三个,炸弹的数量。并记录
        //清空上次统计
        for (ArrayList<Integer> integers : countList) {
            if (integers != null) {
                integers.clear();
            }
        }
        pokerCountMap.forEach((key, value) -> countList.get(value).add(key));
        System.out.println("countList: " + countList);
        //统计王
        Poker[] pokerFive = new Poker[3];//王牌,0不存,1为小,2为大
        int nFive = 0;//王的数量
        for (Poker poker : pokers) {
            String pokerName = poker.toString();
            if (pokerName.startsWith("5")) {
                pokerFive[pokerName.charAt(2) - '0'] = poker;
                nFive++;
            }
        }

        //上次是否为“null”进行分析
        if (strInfo.equals("")) {
            if (local == 1 && !PokerUtil.AUTO_PLAYER) {
                //是玩家,弹起一张小牌
                pokers.get(pokers.size() - 1).setTop(true);
                return true;
            }
            attackStart(handPokers, pokerCountMap, pokerFive, nFive, local, trueThree);
            return true;//一定能出牌
        }
        //上次不为null,对上次牌进行分析(反击部分,已完成)
        String[] split = strInfo.split("&");
        String style = split[0];//全类型
        int oldValue = Integer.parseInt(split[1]);//对应价值
        int mainSign = strInfo.charAt(0) - '0';

        /*----------------开始判断类型-----------------*/
        if (mainSign < 5) {
            //寻找大于的
            int tempSave = -1;
            loop:
            for (int i = mainSign; i <= 4; i++) {
                for (Integer number : countList.get(i)) {
                    if (number > oldValue) {
                        //找到单张牌
                        tempSave = number;
                        break loop;
                    }
                }
                if (i == 1 && nFive == 1) {
                    //第一次循环结束时考虑单张的王
                    //数据类型为1,但没有大值;手上只有一张王,考虑出王
                    //判断手上是小王1还是大王2
                    //如果我不是地主,不用王打队友
                    if (local != trueThree) {
                        int nextLocal = local == 2 ? 0 : local + 1;//下家
                        int lastLocal = nextLocal == 2 ? 0 : nextLocal + 1;//上家
                        //判断上家是否没出
                        if ((split.length == 3 && nextLocal != trueThree) || (split.length == 2 && lastLocal != trueThree)) {
                            //攻击的牌不是地主-》不打
                            System.out.println("return-不是地主不用王打队友");
                            return false;
                        }
                    }
                    int newValue = pokerFive[1] != null ? 20 + 1 : 20 + 2;//v用来装结果
                    if (newValue > oldValue) {
                        //还原为数字,选中
                        pokerFive[newValue - 20].setTop(true);
                        System.out.println("return-单张王");
                        return true;
                    }
                }
            }
            if (tempSave != -1) {
                //证明找到比原先大的数字
                //满足基本条件
                if (style.length() == 1) {
                    //确定同类型,相同数字
                    if (mainSign == 4) {
                        //是炸弹对决
                        //如果我不是地主,不炸队友
                        if (local != trueThree) {
                            int nextLocal = local == 2 ? 0 : local + 1;//下家
                            int lastLocal = nextLocal == 2 ? 0 : nextLocal + 1;//上家
                            //判断上家是否没出
                            if ((split.length == 3 && nextLocal != trueThree) || (split.length == 2 && lastLocal != trueThree)) {
                                //攻击的牌不是地主-》不打
                                System.out.println("return-不是地主去不炸队友炸弹");
                                return false;
                            }
                        }
                    }
                    PokerUtil.useValueSetTopPoker(pokers, tempSave, mainSign);
                    System.out.println("return-同类型,相同数字");
                    return true;
                } else {
                    //类型不同->3带或4带
                    //获取后一个数字
                    int mainCou = Integer.parseInt(style.substring(1, 2));//带的是一张还是两张三张
                    int group = mainSign - 2;//应该带几组(三带拿一组,四带拿两组)
                    //最多能拿出几个合规的组
                    int groupMax = 0;
                    for (int i = mainCou; i <= 3; i++) {
                        groupMax += countList.get(i).size();
                        //如果在找更大的,少算一个(自己)
                        if (mainCou == i) {
                            groupMax--;
                        }
                    }
                    //判断是否满足这么多组
                    if (groupMax >= group) {
                        //选择基本牌
                        PokerUtil.useValueSetTopPoker(pokers, tempSave, mainSign);
                        //选择附带牌
                        int b = 1;
                        int tempMainCou = mainCou;
                        for (int i = 0; i < group; i++) {
                            if (b == 1) {
                                b = PokerUtil.useValueSetTopPoker(pokers, countList.get(tempMainCou).get(i), mainCou);
                                if (b == 0) {
                                    //如果上一个无法选择,不计算次数
                                    group--;
                                    b = 1;//还原
                                }
                            } else if (tempMainCou < 4) {
                                //这次不算,往大的找,重来一次
                                tempMainCou++;
                                i--;
                            } else {
                                System.out.println("return-false(类型不同:3带或4带)");
                                return false;
                            }
                        }
                        System.out.println("return-类型不同:3带或4带");
                        return true;
                    }
                }
            }
        } else {
            //数据类型>5
            if (mainSign == 6) {
                //顺子,获取其他参数
                int group = style.charAt(1) - '0';//顺子的组数
                int count = style.charAt(2) - '0';//每个组的个数
                int start = oldValue + 1;//从老的价值开始往后找
                for (int i = start; i <= 15; i++) {
                    if (i - start == group) {
                        //成功找到从大于等于start小于i的牌组
                        for (int j = 0; j < group; j++, start++) {
                            PokerUtil.useValueSetTopPoker(pokers, start, count);
                        }
                        System.out.println("return-顺子");
                        return true;
                    }
                    if (!keyedSet.contains(i) || pokerCountMap.get(i) < count) {
                        //如果断了只能从后面继续找
                        start = i + 1;
                    }
                }
            } else {
                //三个的顺子带
                //1.获取组数与带的元素信息
                int group = Integer.parseInt(style.substring(1, 2));//顺子组数
                int other = Integer.parseInt(style.substring(2, 3));//带的是单个1还是双2
                int start = oldValue + 1;//从老的价值开始往后找
                for (int i = start; i <= 15; i++) {
                    if (i - start == group) {
                        //成功找到从大于等于start小于i的牌组
                        //先找other
                        if (countList.get(other).size() >= group || (other == 1 && countList.get(1).size() + countList.get(2).size() >= group)) {
                            //确认后,添加
                            int other_ = other;//临时存储,方便调整
                            for (int j = 0; j < group; j++, start++) {
                                PokerUtil.useValueSetTopPoker(pokers, start, 3);
                                PokerUtil.useValueSetTopPoker(pokers, countList.get(other_).get(j), other);
                                if (other == 1 && countList.get(other).size() - 1 == j) {
                                    //1添加失败(为最大下标),2中用1添加
                                    other_++;
                                }
                            }
                            System.out.println("return-顺子");
                            return true;
                        } else {
                            //找不到,退出
                            break;
                        }
                    }
                    if (!keyedSet.contains(i) || pokerCountMap.get(i) < 3) {
                        //如果断了只能从后面继续找
                        start = i + 1;
                    }
                }
            }
        }


        //都不行,考虑炸弹
        //如果电脑不是地主,不炸队友
        if (local != 1 && local != trueThree) {
            int nextLocal = local == 2 ? 0 : local + 1;//下家
            int lastLocal = nextLocal + 1;//上家
            //判断上家是否没出
            if ((split.length == 3 && nextLocal != trueThree) || (split.length == 2 && lastLocal != trueThree)) {
                //攻击的牌不是地主-》不打
                System.out.println("return-不是地主不考虑炸队友");
                return false;
            }
        }

        if (mainSign != 4 && countList.get(4).

                size() > 0) {
            PokerUtil.useValueSetTopPoker(pokers, countList.get(4).get(0), 4);
            System.out.println("return-炸弹强拆");
            return true;
        }

        //考虑王炸
        if (nFive == 2) {
            pokerFive[1].setTop(true);
            pokerFive[2].setTop(true);
            System.out.println("return-王炸");
            return true;
        }
        System.out.println("return-无法出牌");
        return false;
    }

    private static void attackStart(ArrayList<ArrayList<Poker>> handPokers, HashMap<Integer, Integer> pokerCountMap, Poker[] pokerFive, int nFive, int local, int tureThree) {
        ArrayList<Poker> pokers = handPokers.get(local);
        Set<Integer> keyedSet = pokerCountMap.keySet();

        //一、考虑顺子
        for (int count = 3; count > 0; count--) {
            int start = 3;
            for (int number = 3; number <= 15; number++) {
                //如果断开了
                if (!keyedSet.contains(number) || pokerCountMap.get(number) < count) {
                    if ((number - start) * count >= 5) {
                        //1.已经可以出牌了
                        //成功找到从大于等于start小于number的牌组
                        for (int j = start; j < number; j++) {
                            PokerUtil.useValueSetTopPoker(pokers, j, count);
                        }
                        //如果是三连能不能带东西
                        if (count == 3) {
                            int group = number - start;//几组顺子
                            if (countList.get(2).size() >= group || (countList.get(1).size() + countList.get(2).size() >= group)) {
                                int n = 1;//带单张还是两张
                                for (int i = 0; i < group; i++) {
                                    int b = PokerUtil.useValueSetTopPoker(pokers, countList.get(n).get(i), 2);
                                    if (b == -1 && n == 1) {
                                        //这次不算,往大的找
                                        i--;
                                        n++;
                                    }
                                }
                            }
                        }
                        System.out.println("attackStart-顺子");
                        return;
                    } else {
                        //2.重新找
                        start = number + 1;
                    }
                }
            }
        }

        //二、3带、对子或单子
        boolean re = false;//用来判断有没有选中牌
        //有三个就选
        if (countList.get(3).size() > 0) {
            PokerUtil.useValueSetTopPoker(pokers, countList.get(3).get(0), 3);
            re = true;
        }

        //还没选择三张牌,如果我不是地主,地主只有一两张牌
        if (!re && local != tureThree && handPokers.get(tureThree).size() <= 2) {
            //尽量出大牌
            if (countList.get(2).size() > 0 || countList.get(1).size() > 0) {
                int n;//带单张还是两张
                if (countList.get(2).size() > 0 && countList.get(1).size() > 0) {
                    n = countList.get(1).get(0) > countList.get(2).get(0) ? 1 : 2;
                } else {
                    n = countList.get(1).size() > 0 ? 1 : 2;
                }
                PokerUtil.useValueSetTopPoker(pokers, countList.get(n).get(countList.get(n).size() - 1), n);
                re = true;
            }
        } else {
            //有两个或者一个也选,尽量出小牌
            if (countList.get(2).size() > 0 || countList.get(1).size() > 0) {
                int n;//带单张还是两张
                if (countList.get(2).size() > 0 && countList.get(1).size() > 0) {
                    n = countList.get(1).get(0) < countList.get(2).get(0) ? 1 : 2;
                } else {
                    n = countList.get(1).size() > 0 ? 1 : 2;
                }
                PokerUtil.useValueSetTopPoker(pokers, countList.get(n).get(0), n);
                re = true;
            }
        }

        //如果选择了,确定
        if (re) {
            return;
        }

        //三、都没有,出单张王
        if (nFive == 1) {
            if (pokerFive[1] != null) {
                pokerFive[1].setTop(true);
            } else {
                pokerFive[2].setTop(true);
            }
            return;
        }
        //出炸弹
        if (countList.get(4).size() > 0) {
            PokerUtil.useValueSetTopPoker(pokers, countList.get(4).get(0), 4);
            return;
        }

        //只剩王炸,,这可能吗?
        if (nFive == 2) {
            pokerFive[1].setTop(true);
            pokerFive[2].setTop(true);
            return;
        }

        System.out.println("攻击代码走到了最后,出错false");
    }
}


相关文章

网友评论

    本文标题:Java小游戏·斗地主

    本文链接:https://www.haomeiwen.com/subject/zybaidtx.html