精华内容
下载资源
问答
  • 例如: 我 三条和六条 怎么从手牌中找到 四条和五条 三条和六条 怎么从手牌中取出 一对三条 一对六条
  • 这次的听牌算法采用了Java8的StreamAPI新特性进行处理牌组集合,比传统的集合遍历性能上提高了不少,而且还支持并行,这为多线程提供了很好的支持,而且牌组的编码采用了集合工厂,底层省去了修改的功能,创建后不能...

    效果

    判断胡牌
    Java听牌算法判断胡牌
    Java听牌算法判断胡牌

    寻找听牌
    Java听牌算法寻找听牌

    多牌型的寻找胡牌
    Java听牌算法寻找听牌

    这次的听牌算法采用了Java8的StreamAPI新特性进行处理牌组集合,比传统的集合遍历性能上提高了不少,而且还支持并行,这为多线程提供了很好的支持,而且牌组的编码采用了集合工厂,底层省去了修改的功能,创建后不能修改只能遍历,而且赋值时还很方便

    项目源码,jar文件,请前往github获取,https://github.com/ZDG-Kinlon/MahjongCheckHuUtil

    牌组类

    Deck.java
    只是为了对牌组进行转换,方便计算和输出的切换

    package cn.mahjong;
    
    import java.util.ArrayList;
    import java.util.List;
    
    public class Deck {
        //使用集合工厂创建牌组的编号集合
        public static final List<Integer> DECK_NO = List.of(
                11, 12, 13, 14, 15, 16, 17, 18, 19,
                21, 22, 23, 24, 25, 26, 27, 28, 29,
                31, 32, 33, 34, 35, 36, 37, 38, 39,
                41, 42, 43, 44, 45, 46, 47
        );
    
        //使用集合工厂创建牌组的名称集合
        private static final List<String> DECK_STR = List.of(
                "一万", "二万", "三万", "四万", "五万", "六万", "七万", "八万", "九万",
                "一筒", "二筒", "三筒", "四筒", "五筒", "六筒", "七筒", "八筒", "九筒",
                "一条", "二条", "三条", "四条", "五条", "六条", "七条", "八条", "九条",
                "东风", "西风", "南风", "北风", "红中", "白板", "发财"
        );
    
        /**
         * 将牌编号转换为对应的牌名称
         * @param num
         * @return
         * @throws IndexOutOfBoundsException
         */
        public static String getDeckByStr(int num)
          throws IndexOutOfBoundsException {
            return DECK_STR.get(DECK_NO.indexOf(num));
        }
    
        /**
         * 将牌名称转换为对应的牌编号
         * @param str
         * @return
         * @throws IndexOutOfBoundsException
         */
        public synchronized static Integer getDeckByNO(String str)
          throws IndexOutOfBoundsException {
            return DECK_NO.get(DECK_STR.indexOf(str));
        }
    
        /**
         * 将牌集合名称集合转换为牌编号集合
         * @param stringList
         * @return
         * @throws IndexOutOfBoundsException
         */
        public static List<Integer> toDeckNO(List<String> stringList)
          throws IndexOutOfBoundsException {
            List<Integer> out = new ArrayList<>();
            stringList.forEach(s -> out.add(getDeckByNO(s)));
            return out;
        }
    
        /**
         * 将牌集合编号转换为牌名称集合
         * @param intList
         * @return
         * @throws IndexOutOfBoundsException
         */
        public static List<String> toDeckStr(List<Integer> intList)
          throws IndexOutOfBoundsException {
            List<String> out = new ArrayList<>();
            intList.forEach(s -> out.add(getDeckByStr(s)));
            return out;
        }
    }

    判断胡牌的类

    CheckHu.java
    借助StreamAPI对待测牌进行处理,先判断七对子,国士无双这种特殊牌型,如果不成立只能检测是否为3n*2的牌型

    3n的牌型有刻牌和链牌两种,刻牌是三张相同的牌,链牌是连续的同色牌

    因为n最大只能为4,所以在拆牌的时候最多使用4层嵌套,总共16种组合,如果牌型为胡牌,绝对有一种能拆解到最后一张不剩

    拆牌时如果测试牌拆除失败,将会回滚还原,不影响下一次拆除,因为编号没有10,20,30,40号牌,间接做到了区别花色的作用,避免出现拆除链牌时的跨色问题

    Java8引入的StreamAPI在对集合的处理上得到了很大的增强,可以把集合中的元素抽象为Excel表格中每一列的单元格,后面一系列的点方法就是位于表格第一行的筛选和统计功能,很方便的完成对集合的处理

    Java9引入的集合工厂of()方法可以很快的生成不可改变只能查询的集合,效率提升了很多

    package cn.mahjong;
    
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.Comparator;
    import java.util.LinkedList;
    import java.util.List;
    import java.util.Map;
    import java.util.concurrent.Callable;
    import java.util.function.BooleanSupplier;
    import java.util.stream.Collectors;
    
    public class CheckHu implements Callable<Map<Integer, Boolean>> {
        private Integer newDeck;//待测牌
        private List<Integer> checkDeckList;//待测牌集合
        private Map<Integer, Long> map;//每张牌的数量
    
        public CheckHu(List<Integer> checkDeckList) {
            this.checkDeckList = checkDeckList;
        }
    
        public CheckHu(List<Integer> checkDeckList, Integer newDeck) {
            this.newDeck = newDeck;
            this.checkDeckList = checkDeckList;
        }
    
        /**
         * 多线程返回值方法
         *
         * @return
         * @throws Exception
         */
        @Override
        public Map<Integer, Boolean> call() throws Exception {
            return Map.of(newDeck, isHu());
        }
    
        /**
         * 根据待测牌集合判断是否为胡牌牌型
         *
         * @return
         */
        public boolean isHu() {
            return init.getAsBoolean() && (isQiDuiZi.getAsBoolean() ||
               isGuoShiWuShuang.getAsBoolean() || is3n2.getAsBoolean());
        }
    
        //初始化方法
        private BooleanSupplier init = () -> {
            List<Integer> list = new ArrayList<>(this.checkDeckList);
            if (newDeck != null) list.add(newDeck);//如果是另一个待测牌存在,添加进待测集合中
            Collections.sort(list);//必须排序,链牌拆解的时候需要从小到大遍历
            this.checkDeckList = list;
            //使用流统计每张牌的数量
            this.map = list.stream()
              .collect(Collectors.groupingBy(integer -> integer, Collectors.counting()));          
            return !check4count();//判断单张牌是否超过4张
        };
    
        /**
         * 判断单张牌是否超过4张
         *
         * @return
         */
        private boolean check4count() {
        //使用流遍历出单张牌最多的张数
            return this.map.values().stream()
              .max(Comparator.comparing(count -> count)).get().intValue() > 4;
        }
    
        //使用流判断是否为七对子的Lambda表达式,先判断去重复后的牌的数量是否为7张,然后过滤每张牌的个数是为2张的结果的个数是否为7张,
        private BooleanSupplier isQiDuiZi = () -> this.map.keySet().size() == 7 && 
            this.map.values().stream().filter(count -> count == 2).count() == 7;
    
        //使用流判断是否为国士无双的Lambda表达式,先判断去重复后的牌的数量是否为13张,然后过滤每张牌必须为40以上的杂牌,或者40以下的边牌的结果的个数是否为13张
        private BooleanSupplier isGuoShiWuShuang = () -> this.map.keySet().size() == 13 && 
            this.map.keySet().stream()
                .filter(a -> a / 10 == 4 || a % 10 == 9 || a % 10 == 1).count() == 13;
    
        //使用流判断是否为3n+2牌型的Lambda表达式
        private BooleanSupplier is3n2 = () -> {
            int count = checkDeckList.size();//判断待测牌的张数是否满足3n+2,且必须在2至14张之内
            if (count >= 2 && count <= 14 && (count - 2) % 3 == 0) {
                List<Integer> duiDeckList = new ArrayList<>();//使用流遍历找出可能为将牌(2)的所有可能,不是单张牌就添加
                this.map.forEach((a, b) -> {
                    if (b > 1) duiDeckList.add(a);
                });
                for (Integer a : duiDeckList) {//遍历将牌
                    List<Integer> testList = new LinkedList<>(this.checkDeckList);
                    testList.remove(a);//去除两张将牌,然后根据刻牌和链牌进行3n拆解
                    testList.remove(a);
                    if (_A(_A(_A(_A(testList)))).size() == 0) return true;//刻 刻 刻 刻
                    if (_A(_A(_A(_B(testList)))).size() == 0) return true;//链 刻 刻 刻
                    if (_A(_A(_B(_A(testList)))).size() == 0) return true;//刻 链 刻 刻
                    if (_A(_A(_B(_B(testList)))).size() == 0) return true;//链 链 刻 刻
                    if (_A(_B(_A(_A(testList)))).size() == 0) return true;//刻 刻 链 刻
                    if (_A(_B(_A(_B(testList)))).size() == 0) return true;//链 刻 链 刻
                    if (_A(_B(_B(_A(testList)))).size() == 0) return true;//刻 链 链 刻
                    if (_A(_B(_B(_B(testList)))).size() == 0) return true;//链 链 链 刻
                    if (_B(_A(_A(_A(testList)))).size() == 0) return true;//刻 刻 刻 链
                    if (_B(_A(_A(_B(testList)))).size() == 0) return true;//链 刻 刻 链
                    if (_B(_A(_B(_A(testList)))).size() == 0) return true;//刻 链 刻 链
                    if (_B(_A(_B(_B(testList)))).size() == 0) return true;//链 链 刻 链
                    if (_B(_B(_A(_A(testList)))).size() == 0) return true;//刻 刻 链 链
                    if (_B(_B(_A(_B(testList)))).size() == 0) return true;//链 刻 链 链
                    if (_B(_B(_B(_A(testList)))).size() == 0) return true;//刻 链 链 链
                    if (_B(_B(_B(_B(testList)))).size() == 0) return true;//链 链 链 链
                }
            }
            return false;//最多只有4组3n组合,如果都尝试过没有能拆解完毕,则不满足3n+2牌型
        };
    
        /**
         * 刻牌拆解方法
         *
         * @param list
         * @return
         */
        private List<Integer> _A(List<Integer> list) {
            for (Integer integer : list) {//遍历每张牌
                List<Integer> testList = new LinkedList<>(list);//创建测试牌集合
                //尝试拆解,如果拆解成功,返回拆解后的牌集合
                if (breakDeckList(testList, integer, integer, integer)) return testList;
            }
            return list;//拆解失败,返回原先传入的待测牌组
        }
    
        /**
         * 链牌拆解方法
         *
         * @param list
         * @return
         */
        private List<Integer> _B(List<Integer> list) {
            for (Integer integer : list) {//遍历每张牌
                if (integer / 10 == 4) continue;//杂牌无法构成链牌
                List<Integer> testList = new LinkedList<>(list);//创建测试牌集合
                //尝试拆解,如果拆解成功,返回拆解后的牌集合
                if (breakDeckList(testList, integer, integer + 1, integer + 2)) return testList;
            }
            return list;
        }
    
        /**
         * 拆解牌的方法
         *
         * @param list
         * @param i1
         * @param i2
         * @param i3
         * @return
         */
        private boolean breakDeckList(List<Integer> list, Integer i1, Integer i2, Integer i3) {
            //尝试删除传入的三张牌,有一张删除失败返回false
            return list.remove(i1) && list.remove(i2) && list.remove(i3);
        }
    }

    寻找听牌的类

    FindHu.java
    使用线程池,遍历所有牌的可能,与待测牌组合为完整的牌型,然后交给线程去执行,最后返回结果是true的的牌编号的集合

    package cn.mahjong;
    
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Map;
    import java.util.concurrent.Callable;
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.Future;
    
    public class FindHu implements Callable<Map<List<Integer>, List<Integer>>> {
        private List<Integer> integerList;//待寻找的牌组集合
    
        public FindHu(List<Integer> integerList) {
            this.integerList = integerList;
        }
    
        /**
         * 获取听牌集合
         * @return
         */
        public List<Integer> findHu() {
            List<CheckHu> checkHuList = new ArrayList<>();//创建对象集合
            List<Integer> allDeck = Deck.DECK_NO;//获取所有的牌的编号
            //遍历所有牌的编号,传入对象中
            allDeck.forEach(integer -> checkHuList.add(new CheckHu(this.integerList, integer)));
            //创建线程池,每张牌为一个线程
            ExecutorService threadPool = Executors.newFixedThreadPool(allDeck.size());
            List<Future<Map<Integer, Boolean>>> futures = new ArrayList<>();//创建线程执行结果的集合
            try {
                //递交对象任务到线程池,自动调用call方法,返回结果到集合中
                checkHuList.forEach(checkHu -> futures.add(threadPool.submit(checkHu)));
            } finally {
                threadPool.shutdown();//关闭线程池
            }
            List<Integer> out = new ArrayList<>();//创建返回结果的集合
            futures.forEach(f -> {
                try {
                    f.get().forEach((a, b) -> {
                        if (b) out.add(a);//遍历每一个线程的结果,将结果为true对应的牌的编号传入返回集合
                    });
                } catch (InterruptedException | ExecutionException e) {
                    e.printStackTrace();
                }
            });
            return out;//返回结果
        }
    
        /**
         * 多线程遍历多个待测牌的方法
         * @return
         * @throws Exception
         */
        @Override
        public Map<List<Integer>, List<Integer>> call() throws Exception {
            return Map.of(this.integerList, findHu());//key=待测牌集合,value=听牌集合
        }
    }

    寻找多个牌组的听牌的类

    FindHus.java
    同样使用了线程池,对传入的每一个牌组调用寻找听牌的类的方法,多任务同时执行,为了避免线程池泛滥,默认设置最多为4个,建议根据实际硬件情况进行调整

    package cn.mahjong;
    
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.Future;
    
    public class FindHus {
        private List<List<Integer>> lists;
    
        public FindHus(List<List<Integer>> lists) {
            this.lists = lists;
        }
    
        /**
         * 获取待测牌组集合的听牌集合
         * @return
         */
        public Map<List<Integer>, List<Integer>> findHu() {
            ExecutorService threadPool = Executors.newFixedThreadPool(4);//创建线程池
            List<FindHu> findHus = new ArrayList<>();//创建对象
            lists.forEach(list -> findHus.add(new FindHu(list)));//遍历每个传入的待测牌组集合,写入对应的对象中
            List<Future<Map<List<Integer>, List<Integer>>>> futures = new ArrayList<>();//创建每个线程返回结果的集合
            try {
                findHus.forEach(findHu -> futures.add(threadPool.submit(findHu)));//传入对象到线程池中,自动执行call方法,返回结果到集合中
            } finally {
                threadPool.shutdown();//关闭线程池
            }
            Map<List<Integer>, List<Integer>> out = new HashMap<>();//创建返回结果的集合
            futures.forEach(f -> {
                try {
                    f.get().forEach(out::put);//遍历传入的待测牌集合,将听牌集合结果写入
                } catch (InterruptedException | ExecutionException e) {
                    e.printStackTrace();
                }
            });
            return out;
        }
    }

    测试示例

    判断胡牌方法1

    public void checkDemo1() {
        try {
            //1.创建待测牌组,可以不按顺序
            List<String> stringList = List.of("一万", "八万", "一万", "六万", "六万", "一万", "七万", "四万", "五万", "二万", "三万", "六万", "六万", "七万");
            //2.将牌组编号化,如果不存在会抛出异常IndexOutOfBoundsException
            List<Integer> integerList = Deck.toDeckNO(stringList);
            //3.初始化对象
            CheckHu checkHu = new CheckHu(integerList);
            //4.调用方法开始判断
            boolean result = checkHu.isHu();
            //5.输出结果,是胡牌=true
            System.out.println(result);
        } catch (IndexOutOfBoundsException e) {
            e.printStackTrace();
        }
    }

    判断胡牌方法2

    public void checkDemo2() {
        try {
            //1.创建待测牌组,可以不按顺序
            List<String> stringList = List.of("一万", "八万", "一万", "六万", "六万", "一万", "七万", "五万", "二万", "三万", "六万", "六万", "七万");
            //2.创建起到的牌
            String checkDeck = "四万";
            //3.将牌组编号化,如果不存在会抛出异常IndexOutOfBoundsException
            List<Integer> integerList = Deck.toDeckNO(stringList);
            Integer checkDeckNO = Deck.getDeckByNO(checkDeck);
            //4.初始化对象,待测牌组,起到的牌
            CheckHu checkHu = new CheckHu(integerList, checkDeckNO);
            //5.调用方法开始判断
            boolean result = checkHu.isHu();
            //6.输出结果,是胡牌=true
            System.out.println(result);
        } catch (IndexOutOfBoundsException e) {
            e.printStackTrace();
        }
    }

    寻找听牌方法

    public void findDemo1() {
        try {
            //1.创建待测牌组,可以不按顺序
            List<String> stringList = List.of("一万", "一万", "六万", "六万", "七万", "一万", "二万", "五万", "六万", "三万", "四万", "六万", "八万");
            //2.将牌组编号化,如果不存在会抛出异常IndexOutOfBoundsException
            List<Integer> integerList = Deck.toDeckNO(stringList);
            //3.创建对象
            FindHu findHu = new FindHu(integerList);
            //4.调用方法开始寻找
            List<Integer> result = findHu.findHu();
            //5.转换为字符串牌组,如果不存在会抛出异常IndexOutOfBoundsException
            List<String> out = Deck.toDeckStr(result);
            //6.输出结果
            out.forEach(str -> System.out.print(str + "\t"));
        } catch (IndexOutOfBoundsException e) {
            e.printStackTrace();
        }
    }

    寻找多牌组的听牌

    public void findDemo2() {
        try {
            //1.创建待测牌组,可以不按顺序,需要转换为编号牌集合
            List<List<Integer>> lists = List.of(
                    //七对子,听1
                    Deck.toDeckNO(List.of("一万", "一万", "三万", "三万", "六条", "六条", "五筒", "五筒", "东风", "东风", "南风", "南风", "七万")),
                    //国士无双,听13
                    Deck.toDeckNO(List.of("一万", "九万", "一筒", "九筒", "一条", "九条", "东风", "西风", "南风", "北风", "红中", "白板", "发财")),
                    //13听9
                    Deck.toDeckNO(List.of("一万", "一万", "一万", "二万", "三万", "四万", "五万", "六万", "七万", "八万", "九万", "九万", "九万")),
                    //13听8
                    Deck.toDeckNO(List.of("一万", "一万", "一万", "二万", "三万", "四万", "五万", "六万", "六万", "六万", "六万", "七万", "八万")),
                    //10听8
                    Deck.toDeckNO(List.of("三万", "三万", "三万", "四万", "五万", "六万", "七万", "八万", "八万", "八万")),
                    //13听7
                    Deck.toDeckNO(List.of("二万", "二万", "二万", "三万", "四万", "四万", "四万", "四万", "五万", "六万", "七万", "七万", "七万")),
                    //10听7
                    Deck.toDeckNO(List.of("一万", "一万", "一万", "二万", "三万", "四万", "五万", "六万", "六万", "六万")),
                    //10听6
                    Deck.toDeckNO(List.of("三万", "三万", "三万", "四万", "四万", "四万", "五万", "六万", "七万", "八万")),
                    //7听5
                    Deck.toDeckNO(List.of("三万", "三万", "三万", "四万", "五万", "五万", "五万")),
                    //10听4
                    Deck.toDeckNO(List.of("三万", "三万", "四万", "四万", "五万", "五万", "六万", "六万", "七万", "七万")),
                    //7听4
                    Deck.toDeckNO(List.of("三万", "三万", "三万", "四万", "四万", "四万", "五万")),
                    //4听3
                    Deck.toDeckNO(List.of("三万", "三万", "三万", "二万"))
            );
            //2.创建对象,传入待测牌组集合
            FindHus findHus = new FindHus(lists);
            findHus.setnThreads(16);//设置线程池中线程的个数,默认为4
            //3.调用方法开始寻找
            Map<List<Integer>, List<Integer>> result = findHus.findHu();
            //4.对结果进行排序处理,转换为字符串牌组,并输出
            result.forEach((a, b) -> {
                List<String> out1 = Deck.toDeckStr(a);
                List<String> out2 = Deck.toDeckStr(b);
                System.out.print("待测牌:");
                out1.forEach(e -> System.out.print(e + "\t"));
                System.out.print("\n胡牌:");
                out2.forEach(e -> System.out.print(e + "\t"));
                System.out.println("\n");
            });
        } catch (IndexOutOfBoundsException e) {
            e.printStackTrace();
        }
    }
    展开全文
  • 麻将听牌,胡牌算法

    千次阅读 2019-02-19 18:15:43
    最近在研究麻将的算法,自己手写了一份记录一下以后查阅 只实现了听牌,翻数计算还没有做处理,后期在写 麻将万1-9 条11-19 筒21-29 核心:枚举出对子,然后找出刻子,找出顺子 直接上代码: public static ...

    最近在研究麻将的算法,自己手写了一份记录一下以后查阅 只实现了听牌,翻数计算还没有做处理,后期在写

    麻将万1-9

    条11-19

    筒21-29

    核心:枚举出对子,然后找出刻子,找出顺子

    直接上代码:

     public static void main(String[] args) {
            List<Integer> list = new ArrayList<Integer>();
            list.add(4);
            list.add(4);
            list.add(4);
            list.add(14);
            List<Integer> integers = new HuPai().checkTing(list);
            System.err.println(list);
            System.err.println("可以胡的牌:"+integers);
    
        }
    
    
        public List<Integer> checkTing(List<Integer> list){
            List<Integer> hu = new ArrayList<Integer>();
            for(int i = 1;i<30;i++){
                ArrayList<Integer> pai = new ArrayList<Integer>(list);
                pai.add(i);
                List<Integer> integers = removeDuizi(pai);
                if(integers.size()<1){
                    continue;
                }
                for(int j = 0;j<integers.size();j++){
                        //移除对子
                    ArrayList<Integer> pai2 = new ArrayList<Integer>(pai);
    
                    int index =0;
                    Iterator<Integer> it = pai2.iterator();
                    while(it.hasNext()){
                        Integer next = it.next();
                        if (next == integers.get(j)){
                            it.remove();
                            index++;
                        }
                        if (index>1){
                            break;
                        }
                    }
    
    //                System.out.println("对子被移除"+pai2);
    
                    removeKezi(pai2);
    //                System.out.println("移除刻字:"+pai2);
                    Collections.sort(pai2);
                    List<Integer> removeShunzi = removeShunzi(pai2);
    //                System.out.println("结果"+pai2);
                    if(removeShunzi.size()==0){
                        hu.add(i);
                        break;
                    }
                }
            }
            return hu;
        }
    
        //枚举出所有的对子
        public  List<Integer> removeDuizi(List<Integer> pai){
            ArrayList<Integer> integers = new ArrayList<Integer>();
            for(int i = 0;i< pai.size();i++){
                for(int j=i+1;j<pai.size();j++){
                    if(pai.get(i)==pai.get(j)){
                        boolean falg = false;
                       for(int k = 0;k<integers.size();k++){
                                if (pai.get(i) ==integers.get(k) ){
                                    falg=true;
                                    break;
                                }
                       }
                       if(!falg){
                           integers.add(pai.get(i));
                       }
                    }
                }
            }
            return integers;
        }
    
        public List<Integer> removeKezi(List<Integer> pai){
            Map<Integer,Integer> tmpMap = new HashMap<Integer, Integer>();
            Iterator<Integer> it = pai.iterator();
            while (it.hasNext()){
                Integer next = it.next();
                if(tmpMap.get(next) ==null){
                    tmpMap.put(next, 1);
                }else{
                    tmpMap.put(next,tmpMap.get(next)+1 );
                }
            }
    
    
            for(Map.Entry<Integer,Integer> ent : tmpMap.entrySet()){
                if(ent.getValue()>2){
                        int index = 0;
    
                    Iterator<Integer> it2 = pai.iterator();
                    while (it2.hasNext()){
                        Integer next = it2.next();
                        if(ent.getKey()==next){
                                it2.remove();
                                index++;
                        }
                        if(index>2){
                            break;
                        }
                    }
                }
            }
    
            return pai;
        }
    
        public List<Integer> removeShunzi(List<Integer> pai){
            for(int i = 0;i<pai.size();i++){
                for(int j = i+1;j<pai.size();j++){
                    for(int k = j+1; k< pai.size() ; k++){
                        if(pai.get(i)+1==pai.get(j) && pai.get(j)+1 ==pai.get(k)){
                                pai.remove(i);
                                pai.remove(j-1);
                                pai.remove(k-2);
                             return removeShunzi(pai);
                        }
                    }
                }
            }
            return pai;
        }
    
    展开全文
  • 麻将432牌型听牌判断流程图

    千次阅读 2015-09-15 14:46:16
    麻将432牌型听牌判断流程图

    工作交接日,空闲时间画的0层流程图,供后续细化参考:

     

    拆牌方式

     

     

     

     

    展开全文
  • 麻将算法封装,包括牌墙算法、听牌算法、胡牌算法、出牌推荐算法
  • 例,现有"叫"23456,1,4,7。 现在需要将23456亮出来,让其余玩家知道,我的是1,4,7。 如何在算法中检测出23456就是我要亮出的。 还有其余例子 12345671,4,7,需要亮23456 33342,4,5,需要亮3334 3334555...
  • C++实现麻将基本听牌胡牌的算法

    千次阅读 2017-08-03 14:03:10
    c++实现麻将的基本胡牌与听牌算法,包括小七对和十三幺牌型。
    #include <iostream>
    #include <vector>
    #include <algorithm>
    #include <chrono>
    
    enum MajiangType
    {
    	emMJType_Wan	= 1, //万
    	emMJType_Tiao	= 2, //条
    	emMJType_Tong	= 3, //筒
    	emMJType_Zi	= 4, //字
    	emMJType_Hua	= 5  //花
    };
    
    constexpr char MJ(char m, char n) {
    	return m << 5 | n;
    }
    
    inline MajiangType Majiang_Type(char m) {
    	return MajiangType(m >> 5);
    }
    
    inline char Majiang_Value(char m) {
    	return m & 0x1F;
    }
    
    enum emMJ:char
    {
    	emMJ_1Wan = MJ(emMJType_Wan, 1),
    	emMJ_2Wan = MJ(emMJType_Wan, 2),
    	emMJ_3Wan = MJ(emMJType_Wan, 3),
    	emMJ_4Wan = MJ(emMJType_Wan, 4),
    	emMJ_5Wan = MJ(emMJType_Wan, 5),
    	emMJ_6Wan = MJ(emMJType_Wan, 6),
    	emMJ_7Wan = MJ(emMJType_Wan, 7),
    	emMJ_8Wan = MJ(emMJType_Wan, 8),
    	emMJ_9Wan = MJ(emMJType_Wan, 9),
    
    	emMJ_1Tiao = MJ(emMJType_Tiao, 1),
    	emMJ_2Tiao = MJ(emMJType_Tiao, 2),
    	emMJ_3Tiao = MJ(emMJType_Tiao, 3),
    	emMJ_4Tiao = MJ(emMJType_Tiao, 4),
    	emMJ_5Tiao = MJ(emMJType_Tiao, 5),
    	emMJ_6Tiao = MJ(emMJType_Tiao, 6),
    	emMJ_7Tiao = MJ(emMJType_Tiao, 7),
    	emMJ_8Tiao = MJ(emMJType_Tiao, 8),
    	emMJ_9Tiao = MJ(emMJType_Tiao, 9),
    
    	emMJ_1Tong = MJ(emMJType_Tong, 1),
    	emMJ_2Tong = MJ(emMJType_Tong, 2),
    	emMJ_3Tong = MJ(emMJType_Tong, 3),
    	emMJ_4Tong = MJ(emMJType_Tong, 4),
    	emMJ_5Tong = MJ(emMJType_Tong, 5),
    	emMJ_6Tong = MJ(emMJType_Tong, 6),
    	emMJ_7Tong = MJ(emMJType_Tong, 7),
    	emMJ_8Tong = MJ(emMJType_Tong, 8),
    	emMJ_9Tong = MJ(emMJType_Tong, 9),
    
    	//字牌不能形成顺子,所以编码不能连续,而且有的地方有买码规则,字牌也根据除以4的余数被分配到了4个方位
    	emMJ_DongFeng =		MJ(4, 1),//东 1 % 4 = 1
    	emMJ_NanFeng =		MJ(4, 6),//南 6 % 4 = 2
    	emMJ_XiFeng =		MJ(4, 11),//西 11 % 4 = 3
    	emMJ_BeiFeng =		MJ(4, 16),//北 16 % 4 = 0
    	emMJ_HongZhong =	MJ(4, 18),//中 18 % 4 = 2
    	emMJ_FaCai =		MJ(4, 23),//发 23 % 4 = 3
    	emMJ_BaiBan =		MJ(4, 28),//白 28 % 4 = 0
    
    	//一副中花牌各只有一张
    	emMJ_Mei =	MJ(5, 1),//梅
    	emMJ_Lan =	MJ(5, 3),//兰
    	emMJ_Ju =	MJ(5, 5),//菊
    	emMJ_Zhu =	MJ(5, 7),//竹
    	emMJ_Chun = MJ(5, 9),//春
    	emMJ_Xia =	MJ(5, 11),//夏
    	emMJ_Qiu =	MJ(5, 13),//秋
    	emMJ_Dong = MJ(5,15)  //冬
    };
    
    const std::vector<char> all_majiang = {
    	emMJ_1Wan,
    	emMJ_2Wan,
    	emMJ_3Wan,
    	emMJ_4Wan,
    	emMJ_5Wan,
    	emMJ_6Wan,
    	emMJ_7Wan,
    	emMJ_8Wan,
    	emMJ_9Wan,
    
    	emMJ_1Tiao,
    	emMJ_2Tiao,
    	emMJ_3Tiao,
    	emMJ_4Tiao,
    	emMJ_5Tiao,
    	emMJ_6Tiao,
    	emMJ_7Tiao,
    	emMJ_8Tiao,
    	emMJ_9Tiao,
    
    	emMJ_1Tong,
    	emMJ_2Tong,
    	emMJ_3Tong,
    	emMJ_4Tong,
    	emMJ_5Tong,
    	emMJ_6Tong,
    	emMJ_7Tong,
    	emMJ_8Tong,
    	emMJ_9Tong,
    
    	emMJ_DongFeng,
    	emMJ_NanFeng,
    	emMJ_XiFeng,
    	emMJ_BeiFeng,
    	emMJ_BaiBan,
    	emMJ_FaCai,
    	emMJ_HongZhong
    };
    const std::vector<emMJ> pattern131 = { emMJ_1Wan,emMJ_9Wan,emMJ_1Tiao,emMJ_9Tiao,emMJ_1Tong,emMJ_9Tong,
    		emMJ_DongFeng,emMJ_NanFeng,emMJ_XiFeng,emMJ_BeiFeng,emMJ_HongZhong,emMJ_FaCai,emMJ_BaiBan};
    
    //基本和牌类型
    bool IsCommonHu(const std::vector<char>& original_pai)
    {
    	//前提:牌已经排好序,不含已碰牌和已杠牌,所以牌数应该是3n+2
    	//过程:先找出一对将牌,然后再寻找刻子牌和顺子牌,直到剩余牌为0才表示可和牌,否则不能和牌
    
    	//记录将牌位置
    	size_t jiang_location = -1;
    	std::vector<char> pai;
    	while (true)
    	{
    		auto i = jiang_location + 1;
    		if (i >= original_pai.size())
    		{
    			return false;
    		}
    
    		pai = original_pai;
    		if (jiang_location != -1)
    		{
    			if (pai[i] == pai[jiang_location])
    			{
    				++i;
    			}
    		}
    
    		//寻找将牌位置,记录将牌第二个,并擦除该两牌
    		jiang_location = -1;
    		for (; i < pai.size() - 1; ++ i)
    		{
    			if (pai[i] == pai[i + 1])
    			{
    				jiang_location = i + 1;
    				pai.erase(pai.begin() + i, pai.begin() + i + 2);
    				break;
    			}
    		}
    		if (jiang_location == -1)
    		{
    			//没有将牌,不能和牌
    			return false;
    		}
    
    		//剩下的牌数是3的倍数
    		//从左起第1张牌开始,它必须能组成刻子牌或者顺子牌才能和,否则不能和
    		while (pai.size() >= 3)
    		{
    			if (pai[0] == pai[1] && pai[0] == pai[2])
    			{
    				//找到刻子牌并移除
    				pai.erase(pai.begin(), pai.begin() + 3);
    			}
    			else
    			{
    				auto it1 = std::find(pai.begin() + 1,pai.end(), pai[0] + 1);
    				if (it1 != pai.end())
    				{
    					auto it2 = std::find(it1 + 1, pai.end(), pai[0] + 2);
    					if (it2 != pai.end())
    					{
    						//找到顺子牌并移除
    						pai.erase(it2);
    						pai.erase(it1);
    						pai.erase(pai.begin());
    					}
    					else
    					{
    						break;
    					}
    				}
    				else
    				{
    					break;
    				}
    			}
    		}
    
    		if (pai.empty())
    		{
    			break;
    		}
    	}
    	
    	return true;
    }
    
    //是否是十三幺牌型
    bool Is131Hu(std::vector<char> original_pai)
    {
    	if (original_pai.size() != 14)
    	{
    		return false;
    	}
    	size_t i = 0;
    	while (i < original_pai.size()-1)
    	{
    		if (original_pai[i] == original_pai[i+1])
    		{
    			break;
    		}
    		++i;
    	}
    	if (i == original_pai.size() - 1)
    	{
    		return false;
    	}
    
    	original_pai.erase(original_pai.begin() + i);
    	if (pai.size() != pattern131.size()){
    		return false;
    	}
    	for (size_t i = 0; i < pai.size(); ++i)
    	{
    		if (original_pai.[i] != pattern131[i]) {
    			return false;
    		}
    	}
    	return true;
    }
    
    //是否是小七对牌型
    bool Is7pairsHu(const std::vector<char>& pai) {
    	const int pairs = 7;
    	if (pai.size() != 2 * pairs)
    	{
    		return false;
    	}
    	int i = 0;
    	for (; i < pairs; ++i)
    	{
    		if (pai[2 * i] != pai[2 * i + 1])
    		{
    			break;
    		}
    	}
    	return i == pairs;
    }
    
    //检查听哪些牌
    std::vector<char> Ting(const std::vector<char>& pai)
    {
    	//遍历所有牌,依次加入,判断是否能和牌
    	std::vector<char> hupai;
    	for (auto& i : all_majiang)
    	{
    		auto temp(pai);
    		temp.push_back(i);
    
    		std::sort(temp.begin(), temp.end());  //先排序
    		if (Is131Hu(temp) || Is7pairsHu(temp) || IsCommonHu(temp))
    		{
    			hupai.push_back(i);
    		}
    	}
    	return hupai;
    }
    
    int main()
    {
    	std::vector<char> v = { MJ(1,2),MJ(1,2),MJ(2,2),MJ(2,2),MJ(1,3),
    		MJ(3,4),MJ(3,4),MJ(1,4),MJ(1,4),MJ(2,5),MJ(2,5),MJ(1,6),MJ(1,6) };
    
    	std::vector<char> tingpai;
    	auto start = std::chrono::steady_clock::now();
    	tingpai = Ting(v);
    	std::chrono::nanoseconds ns = std::chrono::steady_clock::now() - start;
    	std::cout << "time:" << ns.count() << std::endl;
    	for (auto& i : tingpai)
    	{
    		std::cout << "Result:" << (int)i << std::endl;
    	}
    
    	system("pause");
        	return 0;
    }

    展开全文
  • 编写麻将软件的副产品,用于分析13张麻将听什么
  • C++带赖子的麻将听牌检测算法实现

    千次阅读 2017-09-07 09:51:37
    c++ 对带有赖子的麻将听牌检测算法的实现
  • 一个麻将游戏的听牌规则,只有少量的方法,调用可返回需要打出的牌和胡牌类型
  • 麻将游戏的听牌算法

    万次阅读 2015-11-04 11:04:29
    测试测试!!~~~~这两周都是在测试各种BUG,没事情的时候自己在网上学学新知识,也...不打麻将的童靴一定不知道什么听牌,可是我打麻将最初也不知道什么听牌,霍霍,好丢人啊,估计是以前有东南西北中发白的时候这
  • 和牌算法比较常见,毕竟只要是麻将编程都要用到,后面两种虽然普通的麻将编程用不到,但是要编写AI对策以及某些特殊规则(例如日本麻将)就有用了,尤其一向的算法。 三种算法原理差不多,都是先分析数较少的...
  • 癞子麻将胡牌以及听牌算法实现

    万次阅读 热门讨论 2014-10-24 11:03:29
    需求:碰杠胡 ,不能吃 ,不能 ,只能自摸胡,其中癞子可以做任意但是不能碰和杠。 写的时候还不会玩麻将,还是老板教的。^_^ 最麻烦的是胡牌算法。之前搜到的都是不包含癞子正常的胡牌,用的是%3余2,其中...
  • 我们在玩字牌的时候,打出一张牌的时候会思考接下来听什么牌: 现在有手牌:101 102 103 104 105 106 107 108 109 110 (桌低有跑牌要做将情况) 暂时先不考虑最小和牌胡息就会有以下组合听牌: (101 102 103)...
  • 麻将听牌的算法(java)

    万次阅读 2006-09-06 12:37:00
    近来正在做麻将游戏,写了个判断听牌的算法(暂且称其为算法),和大家分享一下,算法还没通过全面的验证,可能会遗漏某些情况。(我不太会打麻将阿)其中判断和牌算法的程序代码是网上找的,是听牌算法的基石。在这...
  • 随机从麻将中抽取一张做花牌,也就是癞子,求癞子麻将的听牌算法,癞子数目不定,悬赏,如多有高人,请留下联系方式,我会找你联系,多谢。
  • 本程序实现了除广东麻将的全部功能:自动摸牌、打牌、碰、杠、听牌、胡牌(其中庄家手动打牌,具体功能: 系统通过骰子确定庄家,然后发牌,最开始从庄家手动打牌。 可以碰,杠,不能吃牌;没有癞子。只能自摸。...
  • python自制一款炫酷音乐播放器,想啥随便搜!

    万次阅读 多人点赞 2021-07-19 21:57:48
    晚上坐在电脑面前,想着一边撸代码,一边音乐。搜了搜自己想的歌,奈何好多歌曲都提示需要版权,无法播放! 没办法,想歌还是得靠自己解决!今天就一起用python自制一款炫酷的音乐播放器吧~ 首先一起来看看...
  • PM、RD、QA、OP英文缩写是什么意思

    千次阅读 2019-09-25 17:46:57
    这些英文缩写都是什么意思?初入职场或者准备踏入职场的你是不是已经有点犯晕了?今天小编就来给大家科普一下那些起来神秘又高端的英文职位缩写。 1.PM: Product Manager,产品经理,又称品牌经理(Brand Manager...
  • DB 什么意思

    万次阅读 2011-03-01 17:06:00
    dBm 乘 dBm 是什么,1mW 的 1mW 次方?除了同学们老给我写这样几乎可以和歌德巴赫猜想并驾齐驱的表达式外,我活了这么多年也没见过哪个工程领域玩这个。 b/uB  P A?+e= %  :92a34  dB?****β试鲆娴牡...
  • 今天,某妹子突然凑到我的耳边轻声说:“我们公司的程序员,清一色的戴着耳机,你说他们是不是故意不想我们提的需求?”我很方,因为我也喜欢戴耳机。(思考ing)思考了一秒钟后...
  • 编程中常见的Foo,是什么意思。。

    万次阅读 多人点赞 2014-07-28 13:08:34
    语意不清,学起来心里老是有疙疙瘩瘩的感觉,于是查吧,终于揭开了这两个东西的不正常关系,讲个各个处在闹心中的初学者们吧。 参考了众多的网络资料,最后,明确的告诉大家,foo和bar就是外国人的张三、

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 85,214
精华内容 34,085
关键字:

听牌什么意思