自动打牌

Posted yuvejxke

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了自动打牌相关的知识,希望对你有一定的参考价值。

package com.hb.module.ddz.service.ai;

import java.util.ArrayList;

import com.chester.commons.utils.LogUtil;
import com.hb.module.ddz.service.DdzCtrlConfig;
import com.hb.module.ddz.service.DdzLog;

/**
 * 斗地主AI出牌规则
 * 
 * @author pan
 *
 */
public class DdzCardAI extends DdzCard{
    
    public static final int VALUE_DAN = 1;
    public static final int VALUE_DUI = 2;
    public static final int VALUE_SAN = 3;
    public static final int VALUE_SUN = 4;
    public static final int VALUE_SUN_DUI = 5;
    public static final int VALUE_FLY = 6;
    public static final int VALUE_BOMB = 7;
    
    /** 已经出的牌 */
    private int[] sentCards;
    
    /** 出牌者队列 */
    private ArrayList<Integer> sender = new ArrayList<Integer>();
    
    /**
     * 清除所有出牌记录
     */
    public void clearSentCards() {
        sentCards = new int[18];
    }
    
    public int[] getSentCards() {
        return sentCards;
    }
    
    public ArrayList<Integer> getSender(){
        return sender;
    }
    
    /**
     * 添加已经出的牌
     * 
     * @param points
     */
    public void addSentCards(int[] points) {
        for(int i=0;i<points.length;i++) {
            sentCards[points[i]]++;
        }
    }
    
    /**
     * 根据手牌情况获得记牌器
     * 
     * @param cards
     * @return
     */
    public int[] getJpq(int[] cards) {
        int[] points = getPoints(cards);
        int[] jpq = new int[] {
                2 - sentCards[C_D] - sentCards[C_X],
                4 - sentCards[C_2],
                4 - sentCards[C_A],
                4 - sentCards[C_K],
                4 - sentCards[C_Q],
                4 - sentCards[C_J],
                4 - sentCards[C_10],
                4 - sentCards[C_9],
                4 - sentCards[C_8],
                4 - sentCards[C_7],
                4 - sentCards[C_6],
                4 - sentCards[C_5],
                4 - sentCards[C_4],
                4 - sentCards[C_3],
                0//默认炸弹数量为0个,-1为未知数量
        };
        int[] tmp = new int[sentCards.length];
        System.arraycopy(sentCards, 0, tmp, 0, sentCards.length);
        for(int i=0;i<points.length;i++) {
            int p = points[i];
            tmp[p]++;
            switch(p) {
            case C_D:
            case C_X:
                jpq[0]--;
                break;
            default:
                jpq[C_2 - p + 1]--;
                break;
            }
        }
        
        //王炸没出
        if(jpq[0] == 2) {
            jpq[14] = -1;
        }else {
            //是否其它炸弹没出
            for(int i=3;i<=C_2;i++) {
                if(tmp[i]==0) {
                    jpq[14] = -1;
                    break;
                }
            }
        }        
        
        return jpq;
    }
    
    /**
     * 出牌者位置
     * 
     * @param senderSeatId
     */
    public void addSender(int senderSeatId) {
        sender.add(senderSeatId);
    }
    
    /**
     * 寻找当前状态下合适的可出的牌型
     *            
     * @return
     */
    public CardGroup findSendCards(DdzTableState state) {
        
        int parnerPos = state.parnerPos;
        int enemyMinCardNum = state.enemyMinCardNum;
        int sendCardPlayer = state.sendCardPlayer;
        DdzLog.info(-1, "aa");
        //手里剩下的是炸弹
        if(state.isAllBombs()) {            
            if(
                    //只有一个炸弹直接炸
                    state.isOnlyOneBomb() || 
                    //敌人没有王炸
                    !state.hasEnemyBombKingMaybe()) {
                CardGroup cg = useBomb(state);
                if(cg!=null) {
                    return cg;
                }
            }
        }
        //除了炸弹,只剩下一手牌
        CardGroup onlyOneCg = state.getOnlyOneCardGroupNoBomb();        
        if(onlyOneCg!=null) {
            state.setIsLastExceptBomb(true);    
            //最后一手牌数量和敌人最小单牌数量一样的时候,压炸弹(防止牌数量一样时,出单牌、三带等被对方压了)
            if(state.hasBomb() && 
                    state.pointsNoBomb.length == enemyMinCardNum && 
                    //敌人没有王炸
                    !state.hasEnemyBombKingMaybe()){
                CardGroup cg = useBomb(state);
                if(cg!=null) {
                    return cg;
                }
            }
            
            //创建最后一手非炸弹牌,能走就走,有压就压
            if(state.isBigger(onlyOneCg)) {
                return onlyOneCg;
            }
            
        }
        //独立牌型,敌人可能没有王炸,直接使用炸弹
        if(state.isAloneCards() && !state.hasEnemyBombKingMaybe()) {
            CardGroup cg = useBomb(state);
            if(cg!=null) {
                return cg;
            }
        }
        //别人没有出牌,自己出牌
        if (state.lastCard == null) {
            return _autoSendCard(state);
        }
        //压牌
        CardGroup cg = _sendBiggerCard(state);
        if(cg!=null) {
            return cg;
        }
        //检查使用炸弹
        if(
                //队友为上家,敌方手牌只有一张,且我方手牌不出现单牌,压之
                parnerPos==-1 && enemyMinCardNum==1 && getAllDanCards(state.points, false, false)==null ||
                //队友为上家,敌方打出8张以上牌型,且手牌数量小于等于5张,压之
                parnerPos==-1 && sendCardPlayer == 0 && enemyMinCardNum<5 && state.lastCard.points.length >= 8 ||
                //敌方打牌,牌型数量少于7,且连续两轮都是他打牌没人接
                sendCardPlayer == 0 && enemyMinCardNum<7 && state.isSameEnemySender() ||
                //队友为下家,手牌数量为1
                state.isNextParnerOnlyOneCard() ||
                //我方为地主,敌方牌小于5张,且我方单牌和对子数量小于等于1手
                parnerPos==0 && enemyMinCardNum <= 5 && state.getLeftStep() <= 1 ||
                //剩余的牌是很好的牌
                state.isStrong() ||                                                
                //普通的出牌提示,需要提示炸弹
                parnerPos==2    
        ) {
            if(!state.hasEnemyBombKingMaybe()) {
                return useBomb(state);
            }    
        }

        return null;
    }
    
    /**
     * AI主动出牌
     * 
     * @return
     */
    public CardGroup _autoSendCard(DdzTableState state) {
        
        int[] cards = state.cards;
        int[] points = state.points;
        int parnerCardNum = state.parnerCardNum;
        int parnerPos = state.parnerPos;
        int enemyMinCardNum = state.enemyMinCardNum;
        int[] points_no_boms = state.pointsNoBomb;    
        // 队友为下家,手牌数量小于等于2张的时候,帮助队友
        if (parnerPos == 1 && parnerCardNum <= 2) {

            // 队友手牌数量为2,出最小的对子
            if (parnerCardNum == 2 && state.me.getHelp(TYPE_DUI)==-1) {
                int dui = _checkRepeat(points_no_boms, 2, false, -1);
                //设置帮助队友标记
                state.me.setHelp(TYPE_DUI, dui);
                if (dui != -1) {
                    return new CardGroup(new int[] { dui, dui }, TYPE_DUI, dui, cards);
                }
            }
            
            //特殊处理AI优化:
            //自己最小的单牌小于J
            //队友只有一张牌,
            //先检查有没有炸弹,如果有,先出炸弹,以便将倍数翻倍
            if(points_no_boms[0] < C_J) {
                CardGroup cg = useBomb(state);
                if(cg!=null) {
                    return cg;
                }
            }
            
            if(state.me.getHelp(TYPE_DAN)==-1) {
                // 出最小的单牌帮助队友
                state.me.setHelp(TYPE_DAN, points_no_boms[0]);
                return new CardGroup(new int[] { points_no_boms[0] }, TYPE_DAN, points_no_boms[0], cards);    
            }
        
        }
        //============================================================================
        //==================           AUTO SEND CARD                  ===============
        //============================================================================
        
        // 敌方只有一张牌,且有可能比自己手里的大,则出四带2
        // 敌方只有两张牌,且有可能比自己手里的对子大,则出四带2
        if((enemyMinCardNum == 1 && points.length==6 && 
                //最大的单牌和桌面上未出的单牌比较
                state.hasBiggerMy())||
                (enemyMinCardNum==2 && points.length == 8 && 
                //最大的对子和桌面上未出的对子比较
                state.hasBiggerMyDui())) {
            int[] si2 = state.getCutCard().getSi2(-1, -1, C_D);
            if(si2!=null) {
                return new CardGroup(si2,TYPE_SI_2,si2[0],cards);
            }
        }
        
        int pendingCard = -1;
        // 单牌太多,且敌人手牌数量还充足,先走单牌
        ArrayList<Integer> dans = state.getCutCard().dans;
        if(dans.size() > 1 && enemyMinCardNum > 2) {
            int p = dans.get(0);
            if(p < C_J && (!(state.getCutCard().isInSun(p) ||
                    state.getCutCard().isInSan(p) ||
                    state.getCutCard().isInDui(p)) || 
                    !state.hasBiggerMy())) {
                return new CardGroup(new int[] {p},TYPE_DAN,p,cards);                
            }
        }
        
        //单顺
        int[] sun = state.getCutCard().getSun();
        if(sun!=null) {
            if(sun[0] < C_8 || pendingCard==-1 || pendingCard == sun[0])
                return new CardGroup(sun,TYPE_SUN_DAN,sun[0],cards);
        }
        //飞机
        int[] fly = state.getCutCard().getFly(-1,-1,-1,true);
        if(fly!=null) {
            return new CardGroup(fly, TYPE_FLY, fly[0], cards);
        }
        
        //顺对
        int[] sun2 = state.getCutCard().getSun2();
        if(sun2!=null) {
            int num = sun2.length*2;
            int[] tmp = new int[num];
            for (int i = 0, k = 0; i < num / 2; i++, k += 2) {
                tmp[k] = sun2[i];
                tmp[k + 1] = sun2[i];
            }
            if(sun2[0] < C_8 || pendingCard==-1)
                return new CardGroup(tmp,TYPE_SUN_DUI,sun2[0],cards);
        }
        // 三带一、三带二
        int[] san_1 = state.getCutCard().getSan1(-1, false,
                //三带不拆王炸
                cards.length <= 5 ? (state.hasMyBombKing() ? C_X : C_D + 1 ) : 
                    C_2,
                cards.length <= 5 ? C_X : C_2);
        int[] san_2 = state.getCutCard().getSan2(-1, false, true,cards.length <= 6 ? C_X : C_2);
        if (san_1 != null && (
                (san_2 == null || san_2.length == 3) ||
                san_1[3] < C_J ||
                (san_2 !=null && san_2[3] >= C_J)
                )) {
            if(san_1[0] < C_J || pendingCard==-1 || san_1[3] == pendingCard)
                return new CardGroup(san_1, TYPE_SAN_1, san_1[0], cards);
        }else if(san_2!=null){
            return new CardGroup(san_2, san_2.length==3?TYPE_SAN:TYPE_SAN_2, san_2[0], cards);
        }
        
        //敌人不止两张,而自己手里只有两张
        if(enemyMinCardNum>2 && cards.length == 2) {
            int dui = _checkRepeat(points_no_boms, 2, false, -1);
            if(dui!=-1) {//有对子直接出对子
                return new CardGroup(new int[] { dui, dui }, TYPE_DUI, dui, cards);
            }else {//手里是单张
                //从小往大大
                int minIndex = cards[0]>cards[1]?cards[1]:cards[0];
                //直接出单牌,小的
                int dan = DdzCard.getPoint(minIndex);
                return new CardGroup(new int[] { dan}, TYPE_DAN, dan, cards);
            }
        }
        //自己只有2张牌,或者敌方非2张牌的时候,出对子
        if(enemyMinCardNum!=2 || cards.length==2) {
            int dui = _checkRepeat(points_no_boms, 2, false, -1);
            
            //剩余牌大于3张,对子大于10,优先出完单牌
            if(cards.length > 3 && dui>=C_10 && enemyMinCardNum>1) {
                int[] allDans = getAllDanCards(points_no_boms,false,true);
                if(allDans.length > 0 && allDans[0] < dui) {
                    return new CardGroup(new int[] {allDans[0]},TYPE_DAN,allDans[0],cards);
                }
            }
            
            if (dui != -1 && (dui != C_2 || state.getLeftStep() <= 2 || enemyMinCardNum==1)) {
                return new CardGroup(new int[] { dui, dui }, TYPE_DUI, dui, cards);
            }            
        }        
        //敌人牌只有2张,从最大的对子开始打
        if(enemyMinCardNum==2) {
            int dui = -1;
            if(state.getCutCard().getDuiNum()>0) {
                dui = state.getCutCard().duis.get(state.getCutCard().duis.size() - 1);
            }
            if(dui==-1) {
                dui = state.getCutCard().findDui(-1);
            }
            //敌人牌数量是2张,但自己的对子比他大,或者出了对2之后,只剩下一手牌,则可以出
            if(dui!=-1 && state.isMax(TYPE_DUI, dui) && (dui!=C_2 || state.getLeftStep() <= 2)) {
                return new CardGroup(new int[] {dui,dui},TYPE_DUI,dui,cards);    
            }        
        }
        
        //敌人只有一张牌,从最大的开始压
        if (parnerPos != 2 && enemyMinCardNum == 1)  {
            int max_index = points_no_boms.length - 1;
            return new CardGroup(new int[] { points_no_boms[max_index] }, TYPE_DAN, points_no_boms[max_index], cards);
        }
        //敌人有两张牌,自己手里全是单牌,从第二大的开始压
        if (parnerPos != 2 && enemyMinCardNum == 2 && cards.length > 2 && 
                cards.length == state.getCutCard().getDanNum()) {
            int max_index = points_no_boms.length - 2;
            return new CardGroup(new int[] { points_no_boms[max_index] }, TYPE_DAN, points_no_boms[max_index], cards);
        }
        //所有牌都是最大的时候,从最大的开始往下压
        if(cards.length - state.getMaxCount()<=1) {
            // 最大的开始压
            return new CardGroup(new int[] { points_no_boms[points_no_boms.length-1] }, TYPE_DAN, 
                    points_no_boms[points_no_boms.length-1], cards);
        }else
            // 最后出最小的单牌
            return new CardGroup(new int[] { points_no_boms[0] }, TYPE_DAN, points_no_boms[0], cards);
    }

    /**
     * AI压牌,出更大的牌
     * 
     */
    CardGroup _sendBiggerCard(DdzTableState state) {
        try {
            switch (state.lastCard.type) {
            case TYPE_DAN:
                return _sendBiggerDan(state);
            case TYPE_DUI:
                return _sendBiggerDui(state);
            case TYPE_SAN:
                return _sendBiggerSan(state);
            case TYPE_SAN_1:
                return _sendBiggerSan1(state);
            case TYPE_SAN_2:
                return _sendBiggerSan2(state);
            case TYPE_BOMB:
                return _sendBiggerBomb(state);
            case TYPE_SI_2:
                //手里有王炸,则直接出炸弹
                if(state.hasMyBombKing()) {
                    int[] noBombType = DdzCard.getCardType(state.pointsNoBomb);
                    if(noBombType!=null && noBombType[0] == DdzCard.TYPE_DUI) {//对子
                        return useBomb(state);
                    }
                }
                return _sendBiggerSi2(state);
            case TYPE_SUN_DAN:
                return _sendBiggerSunDan(state);
            case TYPE_SUN_DUI:
                return _sendBiggerSunDui(state);
            case TYPE_SUN_SAN:
                return _sendBiggerSunSan(state);
            case TYPE_FLY:
                return _sendBiggerFly(state);
            }
        } catch (Exception e) {
            LogUtil.error(e);
        }
        return null;
    }
    
    /**
     * 对要压出去的牌进行检查,不符合规则的直接不出
     * 
     * @param points
     * @param cg
     * @param leftNum
     * @param sendCardPlayer
     * @param keyPoint
     * @param lastCard
     * @return
     */
    static CardGroup _checkAndSendBigger(int[] points,CardGroup cg,int leftNum,int sendCardPlayer,int keyPoint,CardGroup lastCard,int parnerPos,int parnerCardNum) {        
        // 提示,不进行其他判断
        if(parnerPos == 2) return cg;
        //三带、四带,且为2时
        if(cg.isDai() && cg.keyPoint == DdzCard.C_2) {
            
            int[] tmpPoints = new int[points.length];
            System.arraycopy(points, 0, tmpPoints, 0, tmpPoints.length);
            
            //打出后只有一手牌,直接压
            int[] tmp = DdzCard.deleteArray(cg.points,tmpPoints);
            if(DdzCard.getCardType(tmp)!=null) {
                return cg;
            }
            
            //打出后还剩下4张或者以上单牌,不出
            if(DdzCard.getAllDanCards(points, false, false).length>=4) {
                return null;
            }
        }
        
        //不压队友的J以上的牌型
        if(sendCardPlayer==1 && lastCard.keyPoint>=keyPoint) {
            int[] tmp = DdzCard.deleteArray(cg.points,points);
            if(DdzCard.getCardType(tmp)!=null) {
                return cg;
            }else if(tmp.length < parnerCardNum) {
                //基础牌值是否是强牌值
                boolean isStrong = getBaseCardValues(tmp, true, true) >= 10 ? 
                        (tmp.length<=3 ? true : false) : false;
                if(isStrong)
                    return cg;
                else
                    return null;
            }else {
                return null;
            }
        }
        return cg;    
    }
    
    /**
     * 出单牌
     * 
     * @return
     */
    CardGroup _sendBiggerDan(DdzTableState state) {
        
        int sendCardPlayer = state.sendCardPlayer;
        int[] cards = state.cards;
        int[] points = state.points;
        CardGroup lastCard = state.lastCard;
        int parnerCardNum = state.parnerCardNum;
        int parnerPos = state.parnerPos;
        int enemyMinCardNum = state.enemyMinCardNum;
        
        //队友打牌,且队友是下家,队友只有一张牌的时候,不压
        if(sendCardPlayer == 1 && parnerCardNum==1 && parnerPos == 1) {
            return null;
        }

        //敌人只有一张,队友打牌,且打的牌是最大的了(不包括自己的牌),不压
        if(enemyMinCardNum == 1 && sendCardPlayer == 1 ) {
            if(state.isMax(TYPE_DAN, lastCard.keyPoint)) {
                return null;
            }
        }
        
        //DdzLog.info(-1, "cards.length - state.getMaxCount():%d",cards.length - state.getMaxCount());
        //我手里的牌都是桌面上最大牌了(剩余一张小牌无所谓),且敌人没有炸弹了,压
        if(cards.length - state.getMaxCount() <=1 && !state.hasEnemyBomb()) {
            //TODO 这里要避免拆了王炸
            int[] dans = getAllDanCards2(points, lastCard.keyPoint);
            //DdzLog.info(-1, "dan length:%d",dans.length);
            if(dans.length==0) return null;
            int dan = dans[0];
            //压的牌是大、小王,且有王炸,则直接压王炸
            if(dan >= C_X && state.hasMyBombKing()) {
                CardGroup cg = useBomb(state);
                if(cg!=null) {
                    return cg;
                }
            }
            //从手里最大的开始压
            return new CardGroup(new int[] { dan }, TYPE_DAN, dan, cards);
        }

        //队友打牌,且队友是下家,敌人剩下1张牌的时候,不压
        if(sendCardPlayer == 1 && parnerPos == 1 && enemyMinCardNum == 1) {
            return null;
        }
        
        // 敌方打出单牌,且敌方最小单牌数量等于1时
        if ((sendCardPlayer == 0 && enemyMinCardNum <= 1) || enemyMinCardNum == 1) {
            //TODO BUG,敌人牌只有一张,王炸在自己手里,且自己牌还有不少,不会拆王炸
            // 从大王开始压
            int[] dans = getAllDanCards2(points, lastCard.keyPoint);
            int dan = state.getSuitableCard(dans);        
            if (dan>lastCard.keyPoint) {
                CardGroup cg = new CardGroup(new int[] { dan }, TYPE_DAN, dan, cards);
                return cg;
            }
            //有王炸,但都不是强牌型或者最后一手独立牌型,则压大王
            if(state.hasMyBombKing() && !state.isAloneCards() && !state.isStrong()) {
                CardGroup cg = new CardGroup(new int[] {C_D},TYPE_DAN,C_D,cards);
                return cg;
            }
        }
        else if ( points.length >= 1) {    
            //TODO 这里要判断王炸是否存在
//            
             // 从小到大获得最合适的牌
            int dan = state.getCutCard().getSuitableCard(lastCard.keyPoint, 1);
            
            //拆王炸前先判断是否有AA、22、AAA、222之类的可以拆
            if(dan == -1 || dan >= C_X && state.hasMyBombKing()) {
                int tmp = -1;
                if(parnerPos == 2) {
                    tmp = _checkRepeat(points, 1, false, lastCard.keyPoint);                
                }else {
                    //通过拆牌获得适合的压牌
                    tmp = state.getCutCard().getSuitableCut(lastCard.keyPoint,1,state.sentCardsForMe);
                }    
                if(tmp!=-1) {
                    dan = tmp;
                }
            }
            
            // 敌人出小牌,且非连续2次自己出小牌
            // 牌值大于等于2,需要评估桌面情况
            if(!state.isSameEnemySender() && dan >= DdzCard.C_2 && lastCard.keyPoint < DdzCard.C_K) {
                //拆顺子中多余的牌、222、22获得单牌
                int tmp = state.getCutCard().findDan(lastCard.keyPoint);
                //敌人牌还太多,则使用最小拆牌获得的牌
                if(enemyMinCardNum > 8 || cards.length<=2) {
                    if(tmp == -1 || (tmp >= DdzCard.C_2 && !state.isMax(TYPE_DAN, tmp))) {
                        return null;
                    }                    
                    dan = tmp;
                }
                //DdzLog.info(-1, "last dan:%d",dan);
            }            
            
            //经过一轮操作后,还是要拆王炸
            if(dan >= C_X && state.hasMyBombKing() && enemyMinCardNum > 5) {
                //非强牌或者非独立牌
                if(!state.isStrong() && !state.isAloneCards()) {
                    //判断概率是否拆王炸
                    if(DdzCtrlConfig.isBombKingPass()) {
                        return null;
                    }else if(DdzCtrlConfig.isBombKingUse()) {
                        CardGroup cg = useBomb(state);
                        if(cg!=null) {
                            return cg;
                        }
                    }
                }
            }
            
            if (dan != -1) {
                CardGroup cg = new CardGroup(new int[] { dan }, TYPE_DAN, dan, cards);
                return _checkAndSendBigger(points,cg,1,sendCardPlayer,enemyMinCardNum > 1 ? C_Q : C_K,
                        lastCard,parnerPos,state.parnerCardNum);
            }
        }

        return null;
    }

    /**
     * 出对子
     * 
     * @return
     */
    CardGroup _sendBiggerDui(DdzTableState state) {
        
        int sendCardPlayer = state.sendCardPlayer;
        int[] cards = state.cards;
        int[] points = state.points;
        CardGroup lastCard = state.lastCard;
        int parnerCardNum = state.parnerCardNum;
        int parnerPos = state.parnerPos;
        int enemyMinCardNum = state.enemyMinCardNum;
        
        int[] duis = getAllDuiCards(points, true);
        int dui = state.getSuitableCard(duis);

        //我手里的牌都是桌面上最大牌了(剩余一张小牌或者一个小对无所谓),压
        if (dui > lastCard.keyPoint) {
            //我是农民,地主只有一张牌,队友不是一张牌或者非队友出牌,我手里全是对子,无论怎样都要压对子
            if(parnerPos!=0 && 
                    enemyMinCardNum==1 &&
                    ((sendCardPlayer==1 && parnerCardNum!=1) || sendCardPlayer==0) && 
                    cards.length - duis.length*2 <= 1) {
                CardGroup cg = new CardGroup(new int[] { dui, dui }, TYPE_DUI, dui, cards);
                return cg;                
            }
        }
        
        //敌人出牌,我手里只有3张牌或以下,敌人打对子,检查是否有最大的可压之
        if(sendCardPlayer == 0 && 
                (cards.length<=3 || (cards.length==4 && state.getLeftStep()==2))
                ) {
            if(duis.length==0) return null;
            if (dui > lastCard.keyPoint) {
                CardGroup cg = new CardGroup(new int[] { dui, dui }, TYPE_DUI, dui, cards);
                return cg;
            }
        }
        
        // 队友出牌
        if (sendCardPlayer == 1 && 
                // 敌人剩下的牌小于2张,队友在下家,不压队友
                ((enemyMinCardNum <= 2 && parnerPos == 1) ||
                //队友牌数量小于2张,不压队友
                parnerCardNum<=2)) {
            return null;
        }
        
        if (enemyMinCardNum <= 3) {
            // 敌方手牌数量少于3,队友在上家,优先使用最大牌压
            if (dui > lastCard.keyPoint) {
                CardGroup cg = new CardGroup(new int[] { dui, dui }, TYPE_DUI, dui, cards);
                return cg;
            }
        }
        else if (points.length >= 2) {
            // 否则按正常套路出牌
            // 从最小的对子开始检查,直到找到比目标牌更大的对子
            
            dui = state.getCutCard().getSuitableCard(lastCard.keyPoint,2);
            
            if(dui == -1) {
                if(parnerPos == 2) {
                    dui = _checkRepeat(points, 2, false, lastCard.keyPoint);
                }else {
                    dui = state.getCutCard().getSuitableCut(lastCard.keyPoint, 2, state.sentCardsForMe);
                }    
            }    
            
            //敌方出很小的对子,我出很大的对子,检查是否有多余的连对可拆开来压
            if(dui >= C_A && lastCard.keyPoint < C_10) {
                int tmp = state.getCutCard().findDui(lastCard.keyPoint);
                //敌人牌还太多
                if(enemyMinCardNum>8) {
                    if(tmp == -1 || (dui >= C_A && !state.isMax(TYPE_DUI, tmp))) {
                        return null;
                    }
                    dui = tmp;                    
                }
            }
            
            if (dui != -1) {
                CardGroup cg = new CardGroup(new int[] { dui, dui }, TYPE_DUI, dui, cards);
                return _checkAndSendBigger(points,cg,1,sendCardPlayer,C_J,lastCard,parnerPos,state.parnerCardNum);
            }
        }
        
        return null;
    }

    /**
     * 出三张
     * 
     * @return
     */
    CardGroup _sendBiggerSan(DdzTableState state) {
        
        int sendCardPlayer = state.sendCardPlayer;
        int[] cards = state.cards;
        int[] points = state.points;
        CardGroup lastCard = state.lastCard;
        int parnerPos = state.parnerPos;
        int enemyMinCardNum = state.enemyMinCardNum;
        
        // 敌人就剩下的牌小于3张,不压队友
        if (enemyMinCardNum < 3 && sendCardPlayer == 1 && parnerPos != 2) {
            return null;
        }
        
        //按正常套路压牌
        if (points.length >= 3) {
            int san = state.getCutCard().getSuitableCard(lastCard.keyPoint,3);
            
            if(san == -1 && (parnerPos == 2 || enemyMinCardNum <= 2 && sendCardPlayer == 0)) {
                san = _checkRepeat(points, 3, false, lastCard.keyPoint);                
            }
            
            if (san != -1) {
                CardGroup cg = new CardGroup(new int[] { san, san, san }, TYPE_SAN, san, cards);
                return _checkAndSendBigger(points,cg,1,sendCardPlayer,C_J,lastCard,parnerPos,state.parnerCardNum);
            }
        }

        return null;
    }

    /**
     * 出三带一
     * 
     * @return
     */
    CardGroup _sendBiggerSan1(DdzTableState state) {
        
        int sendCardPlayer = state.sendCardPlayer;
        int[] cards = state.cards;
        int[] points = state.points;
        CardGroup lastCard = state.lastCard;
        int parnerPos = state.parnerPos;
        int enemyMinCardNum = state.enemyMinCardNum;
        
        // 敌人就剩下的牌小于4张,不压队友
        if (enemyMinCardNum < 4 && sendCardPlayer == 1 && parnerPos!=2) {
            return null;
        }
        
        //队友出牌大于J,且自己压完之后,不能一口气打完,则不压
        if (points.length >= 4) {
            int[] san_1 = state.getCutCard().getSan1(lastCard.keyPoint,true,points.length <= 5 ? C_D + 1 : C_2, C_X);        
            if(san_1 == null && (parnerPos == 2 || enemyMinCardNum <= 2 && sendCardPlayer == 0)) {
                san_1 = _checkSan_1(points, false, lastCard.keyPoint);
            }
                        
            if (san_1 != null) {
                CardGroup cg = new CardGroup(san_1, TYPE_SAN_1, san_1[0], cards);
                return _checkAndSendBigger(points,cg,1,sendCardPlayer,C_J,lastCard,parnerPos,state.parnerCardNum);
            }
        }
        
        return null;
    }

    /**
     * 出三带一对
     * 
     * @return
     */
    CardGroup _sendBiggerSan2(DdzTableState state) {

        int sendCardPlayer = state.sendCardPlayer;
        int[] cards = state.cards;
        int[] points = state.points;
        CardGroup lastCard = state.lastCard;
        int parnerPos = state.parnerPos;
        int enemyMinCardNum = state.enemyMinCardNum;
        
        // 敌人就剩下的牌小于5张,不压队友
        if (enemyMinCardNum < 5 && sendCardPlayer == 1 && parnerPos != 2) {
            return null;
        }
        //队友出牌大于J,且自己压完之后,不能一口气打完,则不压
        if (points.length >= 5) {
            int[] san_2 = state.getCutCard().getSan2(lastCard.keyPoint, false, false, C_X);
//            System.out.println("first find:");
//            printArray(san_2);
            if(san_2 == null && (parnerPos == 2 || enemyMinCardNum <= 2 && sendCardPlayer == 0)) {
                san_2 = _checkSan_2(points, false, lastCard.keyPoint);                
            }
//            System.out.println("tow:");
//            printArray(san_2);
            if (san_2 != null) {
                CardGroup cg = new CardGroup(san_2, TYPE_SAN_2, san_2[0], cards);
                return _checkAndSendBigger(points,cg,1,sendCardPlayer,C_J,lastCard,parnerPos,state.parnerCardNum);
            }
        }

        return null;
    }

    /**
     * 出炸弹
     * 
     * @return
     */
    CardGroup _sendBiggerBomb(DdzTableState state) {
        
        int sendCardPlayer = state.sendCardPlayer;
        int[] cards = state.cards;
        int[] points = state.points;
        CardGroup lastCard = state.lastCard;
        int parnerPos = state.parnerPos;
        
        if(sendCardPlayer != 0 && parnerPos != 2) {
            return null;
        }
        
        if (points.length >= 4 && !state.hasBankerBombKing()) {
            int si = _checkRepeat(points, 4, false, lastCard.keyPoint);
            if (si != -1) {
                CardGroup cg = new CardGroup(new int[] { si, si, si, si }, TYPE_BOMB, si, cards);
                return _checkAndSendBigger(points,cg,1,sendCardPlayer,C_J,lastCard,parnerPos,state.parnerCardNum);
            }
        }

        //有王炸,且对方牌很少的时候,压王炸
        if(state.hasMyBombKing() && (state.enemyMinCardNum <= 4)) {
            return useBomb(state);
        }
        
        return null;
    }

    /**
     * 出四带2
     * 
     * @return
     */
    CardGroup _sendBiggerSi2(DdzTableState state) {
        
        int sendCardPlayer = state.sendCardPlayer;
        int[] cards = state.cards;
        int[] points = state.points;
        CardGroup lastCard = state.lastCard;
        int parnerPos = state.parnerPos;
        int enemyMinCardNum = state.enemyMinCardNum;
        
        if (lastCard.cards.length == 6 && points.length >= 6) {
            // 敌人就剩下的牌小于6张,不压队友
            if (enemyMinCardNum < 6 && sendCardPlayer == 1 && parnerPos != 2) {
                return null;
            }
            
            //int[] si_2 = _checkSi_2(points, false, 2, lastCard.keyPoint);
            int[] si_2 = state.getCutCard().getSi2(lastCard.keyPoint, 1,parnerPos == 2 ? C_D + 1 : C_2);
            if(si_2==null && (parnerPos == 2 || enemyMinCardNum <= 2 && sendCardPlayer == 0)) {
                si_2 = _checkSi_2(points,false,2,lastCard.keyPoint);
            }
            
            if (si_2 != null) {
                CardGroup cg = new CardGroup(si_2, TYPE_SI_2, si_2[0], cards);
                return _checkAndSendBigger(points,cg,1,sendCardPlayer,C_J,lastCard,parnerPos,state.parnerCardNum);
            }
        }
        if (lastCard.cards.length == 8 && points.length >= 8) {
            // 敌人就剩下的牌小于8张,不压队友
            if (enemyMinCardNum < 8 && sendCardPlayer == 1 && parnerPos != 2) {
                return null;
            }
            int[] si_2 = state.getCutCard().getSi2(lastCard.keyPoint, 2, parnerPos == 2 ? C_D + 1 : C_2);
            if(si_2==null && (parnerPos == 2 || enemyMinCardNum <= 2 && sendCardPlayer == 0)) {
                si_2 = _checkSi_2(points, false, 4, lastCard.keyPoint);
            }
            
            if (si_2 != null) {
                CardGroup cg = new CardGroup(si_2, TYPE_SI_2, si_2[0], cards);
                return _checkAndSendBigger(points,cg,1,sendCardPlayer,C_J,lastCard,parnerPos,state.parnerCardNum);
            }
        }

        return null;
    }

    /**
     * 出单顺
     * 
     * @return
     */
    CardGroup _sendBiggerSunDan(DdzTableState state) {

        int sendCardPlayer = state.sendCardPlayer;
        int[] cards = state.cards;
        int[] points = state.points;
        CardGroup lastCard = state.lastCard;
        int parnerPos = state.parnerPos;
        int enemyMinCardNum = state.enemyMinCardNum;
        
        // 敌人就剩下的牌小于要压的牌数量,不压队友
        if (enemyMinCardNum < points.length && sendCardPlayer == 1 && parnerPos != 2) {
            return null;
        }
        
        int[] points_tmp = points;
        while (points_tmp.length >= lastCard.points.length) {
            int[] sun = _checkSun(points_tmp, 1, false, true);
            if (sun == null) {
                break;
            }
            int num = lastCard.points.length;
            if (sun[0] > lastCard.keyPoint && sun.length >= num) {
                int[] tmp = new int[num];
                for (int i = 0; i < num; i++) {
                    tmp[i] = sun[i];
                }
                CardGroup cg = new CardGroup(tmp, TYPE_SUN_DAN, sun[0], cards);
                return _checkAndSendBigger(points,cg,1,sendCardPlayer,C_7,lastCard,parnerPos,state.parnerCardNum);
            } else {
                // 移除最小的牌,继续寻找其他顺子
                points_tmp = deleteArray(new int[] { sun[0] }, points_tmp);
            }
        }
        return null;
    }

    /**
     * 出顺对
     * 
     * @return
     */
    CardGroup _sendBiggerSunDui(DdzTableState state) {

        int sendCardPlayer = state.sendCardPlayer;
        int[] cards = state.cards;
        int[] points = state.points;
        CardGroup lastCard = state.lastCard;
        int parnerPos = state.parnerPos;
        int enemyMinCardNum = state.enemyMinCardNum;
        
        // 敌人就剩下的牌小于要压的牌数量,不压队友
        if (enemyMinCardNum < points.length && sendCardPlayer == 1 && parnerPos != 2) {
            return null;
        }
        
        int[] points_tmp = points;
        while (points_tmp.length >= lastCard.points.length) {
            int[] sun = _checkSun(points_tmp, 2, false, true);
            if (sun == null) {
                break;
            }
            int num = lastCard.points.length;
            if (sun[0] > lastCard.keyPoint && sun.length * 2 >= num) {
                int[] tmp = new int[num];
                for (int i = 0, k = 0; i < num / 2; i++, k += 2) {
                    tmp[k] = sun[i];
                    tmp[k + 1] = sun[i];
                }
                CardGroup cg = new CardGroup(tmp, TYPE_SUN_DUI, sun[0], cards);
                return _checkAndSendBigger(points,cg,1,sendCardPlayer,C_8,lastCard,parnerPos,state.parnerCardNum);
            } else {
                // 移除最小的对子,继续寻找其他顺子
                points_tmp = deleteArray(new int[] { sun[0], sun[0] }, points_tmp);
            }
        }
        return null;
    }

    /**
     * 出三顺
     * 
     * @return
     */
    CardGroup _sendBiggerSunSan(DdzTableState state) {
        
        int sendCardPlayer = state.sendCardPlayer;
        int[] cards = state.cards;
        int[] points = state.points;
        CardGroup lastCard = state.lastCard;
        int parnerPos = state.parnerPos;
        int enemyMinCardNum = state.enemyMinCardNum;
        
        // 敌人就剩下的牌小于要压的牌数量,不压队友
        if (enemyMinCardNum < points.length && sendCardPlayer == 1 && parnerPos != 2) {
            return null;
        }
        
        int[] points_tmp = points;
        while (points_tmp.length >= lastCard.points.length) {
            int[] sun = _checkSun(points_tmp, 3, false, true);
            if (sun == null) {
                break;
            }
            int num = lastCard.points.length;
            if (sun[0] > lastCard.keyPoint && sun.length * 3 >= num) {
                int[] tmp = new int[num];
                for (int i = 0, k = 0; i < num / 3; i++, k += 3) {
                    tmp[k] = sun[i];
                    tmp[k + 1] = sun[i];
                    tmp[k + 2] = sun[i];
                }
                CardGroup cg = new CardGroup(tmp, TYPE_SUN_SAN, sun[0], cards);
                return _checkAndSendBigger(points,cg,1,sendCardPlayer,C_10,lastCard,parnerPos,state.parnerCardNum);
            } else {
                // 移除最小的三条,继续寻找其他顺子
                points_tmp = deleteArray(new int[] { sun[0], sun[0], sun[0] }, points_tmp);
            }
        }
        return null;
    }

    /**
     * 出飞机
     * 
     * @return
     */
    CardGroup _sendBiggerFly(DdzTableState state) {
        
        int sendCardPlayer = state.sendCardPlayer;
        int[] cards = state.cards;
        int[] points = state.points;
        int enemyMinCardNum = state.enemyMinCardNum;
        CardGroup lastCard = state.lastCard;
        int parnerPos = state.parnerPos;
        
        // 敌人就剩下的牌小于要压的牌数量,不压队友
        if (sendCardPlayer == 1 && parnerPos != 2) {
            return null;
        }
        
        int[] sun = _checkSun(lastCard.points, 3, false, false);
        if (sun == null) {
            return null;
        }
        int sunLen = sun.length;
        int num = lastCard.points.length - sunLen*3;    
        if(num == sunLen) {
            num = 1;
        }else {
            num = 2;
        }
        
        int[] fly = state.getCutCard().getFly(lastCard.keyPoint,sunLen,num,true);
        
        if(fly==null && (parnerPos == 2 || enemyMinCardNum <= 2)) {
            fly = _checkFly(points,false);
        }
        
        if(fly!=null) {
            CardGroup cg = new CardGroup(fly, TYPE_FLY, fly[0], cards);
            if(parnerPos!=2) {
                if(cg.keyPoint > lastCard.keyPoint && fly.length == lastCard.cards.length) {
                    return cg;
                }
            }else
                return cg;
        }
        
        return null;
    }
}

 

以上是关于自动打牌的主要内容,如果未能解决你的问题,请参考以下文章

postman 自动生成 curl 代码片段

postman 自动生成 curl 代码片段

如何设置 vscode 的代码片段,以便在自动完成后自动触发 vscode 的智能感知?

Java 续上上表 和某位知名老总打牌,赢下价值两百万的Java核心知识,今天发出来,交交善缘。

Java 续上上表 和某位知名老总打牌,赢下价值两百万的Java核心知识,今天发出来,交交善缘。

VSCode 配置 用户自定义代码片段 自定义自动代码补充