⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 mjcardstat.java

📁 麻将程序
💻 JAVA
字号:
package com.newpalm.game.mj.share;

import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.Vector;

/**
 * @author liyamin
 * 静态方法集,用来对一组牌进行分析
 */
public class MJCardStat {

        /**
         * 胡牌判断,特殊胡法当然需要特殊处理,这里处理平胡判断
         * 判断逻辑:
         * 1、首先,只需要判断手中省下的牌,吃出、碰出等的牌不需要判断
         * 2、如果手牌数量是1、4、7(%3==1)张,是不可能胡的
         * 3、将手牌分成多组连续牌,比如
         * {{1w,2w,2w,3w},{3b,4b,5b,5b,6b,7b,7b,7b},{中,中},{发}},
         * 字牌没有顺子,所以每个字牌做一组
         *
         * 4、依次对每组连续牌做判断
         * 4.1统计连续牌的数量特性,比如上面那组牌,统计为{"121", "11213","2","1"}
         * 有了这个特性值,我们不用考虑"121"对应的是"1w,2w,2w,3w",还是"5w,6w,6w,7w",
         * 这为后面递归判断简化了输入信息
         *
         * 4.2连续牌分两种情况,一种是带雀头的,这时连续牌的数量是2、5、8(%3==2)
         * 不带雀头的数量是3、6、9(%3==0)
         * 对带雀头的情况,拿走一对雀头后,就可以当不带雀头的连续牌判断了。
         * 哪对是雀头?穷举法,依次尝试拿走重复数大于2的牌,剩下的做"不带雀头的连续牌判断",
         * 只要有一种组合是好的就行
         *
         * 4.3不带雀头的连续牌判断
         * 4.3.1这里是递归判断,首先要确保进入条件是“不带雀头的连续牌”
         * 4.3.2如果连续牌的数量不是3、6、9,返回false
         * 4.3.3从连续牌的两头,三个一组,三个一组的拿走“顺子”,“刻子”,
         * 需要注意,剩下的牌不能破坏“连续牌”这个进入条件
         * 4.3.4如果能拿光所有牌,则是正确的组合
         *
         * 4.3.5例子:
         * "2w,3w,3w,4w,4w,5w,5w,5w,5w,6w,6w,6w"的特征值是"12243"
         * 首先,4个重复牌不需要判断位置,直接拿走3个,"12213"(拿走一组5w的刻子)
         * 如果是两头的3,则拿走,"12210"(拿走一组6w的刻子)
         * 拿走边上的顺子,可以从左边,也可以从右边,"01110"(拿走2,3,4w的顺子)
         * 拿走边上的顺子,"00000",(拿走3,4,5w的顺子)
         * 没有剩余牌,所以是正确的组合:)
         *
         * @param cards
         * @return
         */
        public static boolean isHuable(List cards) {
                if (cards.size() % 3 != 2)
                        return false;
                if (cards.size() == 2 && cards.get(0).equals(cards.get(1)))
                        return true;

                boolean rt = false;
                Collection cardsCol = MJCardStat.splitSequential(cards, 1);

                boolean is7pair = true;
                for (Iterator iter = cardsCol.iterator(); iter.hasNext();) {
                        Collection seq = (Collection) iter.next();
                        int[] stat = MJCardStat.statSequence(seq);
                        for (int i = 0; i < stat.length; i++) {
                                if (stat[i] != 2 || stat[i] != 4) {
                                        is7pair = false;
                                        break;
                                }
                        }
                        if (!is7pair)
                                break;
                }
                if (is7pair)
                        return true;

                int headCount = 0;
                for (Iterator iter = cardsCol.iterator(); iter.hasNext();) {
                        Collection seq = (Collection) iter.next();
                        int n = MJCardStat.analyseSequence(seq);
                        if (n == -1)
                                return false;
                        if (n == 0) {
                                rt = true;
                        }
                        if (n == 1)
                                headCount++;
                        if (headCount >= 2)
                                return false;
                }

                if (headCount != 1)
                        return false;
                return rt;
        }

        /**
         * 选取符合type的牌
         * @param type @see MJCard#getType()
         */
    public static Collection selectByType(Collection cards, int type) {
        Collection rt = new Vector();
        for (Iterator iter = cards.iterator(); iter.hasNext();) {
            MJCard card = (MJCard) iter.next();
            if (card.getType() == type) {
                rt.add(card);
            }
        }
        return rt;
    }

        /**
         * 将一组牌分割成多组连续牌,比如,将
         * {3b,4b,5b,5b,1w,2w,2w,3w, 6b,中,发, 中, 7b,7b,7b}
         * 分成
         * {{1w,2w,2w,3w},{3b,4b,5b,5b,6b,7b,7b,7b},{中,中},{发}}
         * @param cards
         * @param interval
         * @return 2维的Collection,叶子元素是MJCard
         */
    public static Collection splitSequential(Collection cards, int interval) {
        Collection rt = new Vector();
        SortedSet set;
        if (cards instanceof SortedSet) {
            set = (SortedSet) cards;
        } else {
            set = new TreeSet(cards);
        }

        int lastPoint = -interval - 1;
        int lastType = MJCard.TYPE_BIN;
        Collection subCol = null;
        for (Iterator iter = set.iterator(); iter.hasNext();) {
            MJCard card = (MJCard) iter.next();
            if (card.getPoint() - lastPoint > interval
            || card.getType() != lastType
            || (card.getPoint() != lastPoint && card.getType() == MJCard.TYPE_FENG)
            || (card.getPoint() != lastPoint && card.getType() == MJCard.TYPE_ZFB)) {
                subCol = new Vector();
                rt.add(subCol);
            }
            lastPoint = card.getPoint();
            lastType = card.getType();
            subCol.add(card);
        }

        return rt;
    }

    /**
     *
     * @param cards
     * @return -1 impossible
     * 			0 possible without head
     * 			1 possible and with head
     */
    public static int analyseSequence(Collection cards) {
        if (cards.size() % 3 == 1) {
            return -1;
        }

        int[] stat = MJCardStat.statSequence(cards);
        if (cards.size() % 3 == 0) { // without head test
            if (analyseNoHeadStat(stat))
                      return 0;
            else
                    return -1;
        } else { // with head
                // 抽取两张重复数大于2的牌,剩下的分析和无将组合的分析相同,
                // 只要有一组能成,就行。
            for (int i = 0; i < stat.length; i++) {
                if (stat[i] > 2
                        || (stat[i]==2 && (i==0 || i==stat.length-1)) ) {
                        int[] tmpstat = new int[stat.length];
                        System.arraycopy(stat, 0, tmpstat, 0, stat.length);
                    tmpstat[i] -= 2;
                    if (analyseNoHeadStat(tmpstat))
                                                return 1;
                } else if (stat[i] == 2 && stat.length > 2) {
                        // break the seq to two parts
                        int[] part1 = new int[i];
                        int[] part2 = new int[stat.length-i-1];
                        System.arraycopy(stat, 0, part1, 0, part1.length);
                        System.arraycopy(stat, i+1, part2, 0, part2.length);
                        if (analyseNoHeadStat(part1)
                                && analyseNoHeadStat(part2))
                                return 1;
                }
            } // for
            return -1;
        }
    }

    private static boolean analyseNoHeadStat(int[] stat) {
        stat = MJCardStat.replace4with1(stat);
        stat = MJCardStat.predigestStat(stat);
        if (stat == null || stat.length == 0)
            return true;
        else
            return false;
    }

        /**
         * 统计连续牌的特性值,比如
         * {2w,3w,3w,4w,4w,5w,5w,5w,5w,6w,6w,6w}的特征值是"12243",
         * 表示1个2w,2个3w,2个4w,4个5w,3个6w
         * @param cards
         * @return
         * @see splitSequential
         */
    public static int[] statSequence(Collection cards) {
        int[] rt = new int[cards.size()];
        MJCard lastCard = null;
        int count = 0;
        int position = -1;

        for (Iterator iter = cards.iterator(); iter.hasNext();) {
            MJCard card = (MJCard) iter.next();
            if (!card.equals(lastCard)) {
                if (position != -1)
                    rt[position] = count;
                count = 1;
                position++;
                lastCard = card;
            } else {
                count++;
            }
        }
        if (position >= 0) // add the last count
            rt[position] = count;

        int[] trimedRt = new int[position + 1];
        System.arraycopy(rt, 0, trimedRt, 0, position + 1);
        return trimedRt;
    }

        /**
         * 模拟从连续牌两边取“顺子”,“刻子”的操作
         * 例子:
         * "2w,3w,3w,4w,4w,5w,5w,5w,5w,6w,6w,6w"的特征值是"12243"
         * 首先,4个重复牌不需要判断位置,直接拿走3个,"12213"(拿走一组5w的刻子)
         * 如果是两头的3,则拿走,"12210"(拿走一组6w的刻子)
         * 拿走边上的顺子,可以从左边,也可以从右边,"01110"(拿走2,3,4w的顺子)
         * 拿走边上的顺子,"00000",(拿走3,4,5w的顺子)
         * 没有剩余牌,所以是正确的组合:)
         * @param stat
         * @return
         */
    private static int[] predigestStat(int[] stat) {
        int[] ts = trimZero(stat);

        if (ts == null || ts.length == 0)
            return null; // 递归返回
        else if (ts.length == 1) {
            if (ts[0] == 3)
                return null;
            else
                return ts;
        } else if (ts.length == 2) {
            if (ts[0] == 3 && ts[1] == 3)
                return null;
            else
                return ts;
        } else if (ts.length >= 3) { // 221, 121, 231, 232开头的
                if (ts[0] != 3 && Math.max(ts[0], ts[1]) > ts[2])
                        return ts;
                        if (ts[ts.length-1] != 3
                                &&Math.max(ts[ts.length-1], ts[ts.length-2]) > ts[ts.length-3])
                                return ts;
        }

        int[] oldValue = new int[ts.length];
        System.arraycopy(ts, 0, oldValue, 0, ts.length);

        if (ts[0] == 3) {
            ts[0] = 0;
        } else if (ts[ts.length - 1] == 3) {
            ts[ts.length - 1] = 0;
        } else if (ts.length == 3) {
            if (ts[0] > 0 && ts[1] > 0 && ts[2] > 0) {
                ts[0]--;
                ts[1]--;
                ts[2]--;
            }
        } else if (ts.length > 3) {
            if (ts[0] <= ts[1] && ts[1] <= ts[2]) {
                ts[0]--;
                ts[1]--;
                ts[2]--;
            }
            if (ts[ts.length - 1] <= ts[ts.length - 2]
                && ts[ts.length - 2] <= ts[ts.length - 3]) {
                ts[ts.length - 1]--;
                ts[ts.length - 2]--;
                ts[ts.length - 3]--;
            }

        }

                if (ts.length == oldValue.length) {
                        int i = 0;
                        for (; i < oldValue.length; i++) {
                                if (oldValue[i]!=ts[i])
                                        break;
                        }
                        if (i==oldValue.length) {
                                System.err.println("dead loop detected.(MJCardStat.predigestStat()):" + ts);
                                return ts;
                        }
                }

        return predigestStat(ts);
    }

    private static int[] trimZero(int[] stat) {
        if (stat == null || stat.length == 0)
            return null;

        int leftZero = 0;
        for (int i = 0; i < stat.length; i++) {
            if (stat[i] == 0)
                leftZero++;
            else
                break;
        }
        if (leftZero == stat.length) // all zero
            return null;

        int rightZero = 0;
        for (int i = stat.length - 1; i >= 0; i--) {
            if (stat[i] == 0)
                rightZero++;
            else
                break;
        }

        // ts trimedStat
        int[] ts;
        if (leftZero + rightZero == 0)
            ts = stat;
        else {
            ts = new int[stat.length - leftZero - rightZero];
            System.arraycopy(stat, leftZero, ts, 0, ts.length);
        }
        return ts;
    }

    private static int[] replace4with1(int[] stat) {
        if (stat == null || stat.length == 0)
            return null;
        for (int i = 0; i < stat.length; i++) {
            if (stat[i] == 4)
                stat[i] = 1;
        }
        return stat;
    }
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -