麻将算法之 ------ 胡牌算法

2023-05-16

麻将数据牌集合

private int[] cardDataArray =
        {
        0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, //万子 1 2 3 4 5 6 7 8 9
        0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, //索子 17 18 19 20 21 22 23 24 25
        0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, //同子 33 34 35 36 37 38 39 40 41
        0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, //番子        49 50 51 52 53 54 55
        //0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,//花
        };

思路 :麻将胡牌算法为3n*2(其中2为将牌、3n为顺子或者刻子)
1、首先查找所有可以作为将牌的牌。用剩余的牌来判断是否是可以胡的。
2、剩余的牌打上断点、用数量作为值来判断是否符合顺子或者刻子的算法。
3、符合返回true 不符合返回false.

代码如下

 // cbHandCard 为自己的手牌   cacheCard 为别人打得牌或者自摸得到的牌
 public bool huLegal(int cacheCard, int[] cbHandCard)
 {
 //获取手牌数组长度
 int first = cbHandCard.Length;    

 // 判断自己手牌的张数  mianNeed = 0 表示玩家剩余一张牌、不需要去判断将牌
 int mianNeed = NeedMain(cbHandCard);

 //新建集合、将所有的牌存入进去、来判断是否可以胡
 int[] card = new int[first + 1];
 for (int i = 0; i < first; i++)
 {
     card[i] = cbHandCard[i];
 }
 card[first] = cacheCard;
 SortCardList(card); //排序

 if (mianNeed == 0)
 {
   //表示玩家只剩余一张牌(只需判断和另外一张牌是否相等)
    if (cbHandCard[0] == cacheCard) return true;
    else return false; 
 }
 else
 {
   //手中牌不符合胡牌规则 3n*2
   if ((first + 1) % 3 != 2) return false;
   else
   {
       //提取出所有可能为将的牌。其余长度用0补全
       int[] jiangs = getJiang(card, first + 1);

       //去除将牌  mians 是去除掉将牌后的牌 (因为可能有多个将牌 所以为集合)
       List<int[]> mians = quJiangArrs(card, jiangs, first + 1);


      //当前去除将牌后牌的长度
      int size = mians.Count;
      //将去除将牌后分段是否符合可胡牌型的bool值
      bool[] ones = new bool[size];
      //每一个断点的所有牌的集合
      int[] breaks;


     for (int i = 0; i < size; i++)
     {
         //根据打的断点 获取到第一个长度的所有牌型
         breaks = getBreaks(mians[i], first - 1);

         if (breaksCheck(breaks, breaks.Length))
         {
         //获取到的牌不符合3n的格式、直接为false
          ones[i] = false;
         }
         else
         {
            //如果断点断的牌符合顺子或者刻字(3n)的格式
            int[] miansparts = allParts(mians[i], breaks);

            ones[i] = finalHu(miansparts, miansparts.Length);
          }
       }

        for (int i = 0; i < size; i++)
        {
           if (ones[i]) return true;
        }

     }
   }

       return false;
}

接下来为上个方法所用到的一些方法

    /// 是否需要将牌(玩家剩余一张牌是不需要)
    int NeedMain(int[] cbHandCard)
    {
        int last = cbHandCard.Length;
        if (last <= 1)
        {
            return 0;
        }
        return 1;
    }



    /// 排序手中的牌
    public void SortCardList(int[] handCard)
    {
        List<int> sortTemp = new List<int>(handCard);
        sortTemp.Sort();
        for (int i = 0; i < sortTemp.Count; i++)
        {
            handCard[i] = sortTemp[i];
        }
    }




    /// 查找所有的将牌 
    int[] getJiang(int[] cbHandCard, int cbHandCardCount)
    {
        int[] arr = new int[cbHandCardCount];
        int count = 1;

        for (int i = 1; i < cbHandCardCount; i++)
        {
            if (cbHandCard[i] == cbHandCard[i - 1])
            {
                if (count == 1) arr[i - 1] = cbHandCard[i];
                count += 1;
            }
            else count = 1;
        }
        return arr;
    }





    /// 去除将牌、返回所有牌的情况(因为可能有多种牌做奖牌)、
    // List 集合中每一个索引对应的数组都是一种排列方式
    List<int[]> quJiangArrs(int[] card, int[] jiangs, int length)
    {
        List<int[]> list = new List<int[]>();

        for (int i = 0; i < length; i++)
        {
            if (jiangs[i] != 0)
            {
                int[] src = new int[length];
                int[] arr = new int[length - 2];
                for (int j = 0; j < length; j++)
                {
                    src[j] = card[j];
                }

                src[i] = 0x00;
                src[i + 1] = 0x00;

                SortCardList(src);
                Array.Copy(src, 2, arr, 0, length - 2);
                list.Add(arr);
            }
        }
        return list;
    }



    /// 查找去除将牌后的断点(没有连续的牌就是一个断点)
    /// 返回的数组为断点所在索引的集合 例如(3 4 5 7 8 9) 断点集合{0,3,6}
    int[] getBreaks(int[] cbHandCard, int cbHandCardCount)
    {
        int[] breaks = new int[22];//首位0
        int count = 1;
        for (int i = 1; i < cbHandCardCount; i++)
        {
            if ((cbHandCard[i] - cbHandCard[i - 1]) > 1)
            {
                breaks[count] = i;
                count += 1;
            }
        }
        breaks[count] = cbHandCardCount;


        int[] breakss = new int[count + 1];//首位0 尾位length

        for (int i = 0; i < count + 1; i++)
        {
            breakss[i] = breaks[i];
        }


        return breakss;


    }



     /// <summary>
    /// 断点后、检查所断的牌型是不是顺子或者刻子
    /// 手牌 3 4 5 7 8 9  -- 断点后  0  3  6  (将牌已被丢出)
    /// </summary>
    /// <param name="breaks">  breaks 为断点后 下标的集合</param>
    /// <param name="length"></param>
    /// <returns> false 表示断牌为顺子或者刻字  true表示不符合胡牌规则  </returns>
    bool breaksCheck(int[] breaks, int length)
    {

        for (int i = 1; i < length; i++)
        {
            if (breaks[i] % 3 != 0)
                return true;
        }
        return false;
    }




     /// <summary>
    /// 根据手牌和断点的数组 返回经过处理的牌型
    /// </summary>
    /// <param name="cbHandCard">  cbHandCard -- 去除将牌后的手牌</param>
    /// <param name="breaks">  breaks -- 标记断点后的索引</param>
    /// <returns></returns>
    int[] allParts(int[] cbHandCard, int[] breaks)
    {

        int partnum = breaks.Length - 1;  //断点个数

        int[] parts = new int[partnum];
        int[] arr;
        for (int i = 0; i < partnum; i++)
        {

            arr = new int[breaks[i + 1] - breaks[i]];

            for (int j = 0; j < breaks[i + 1] - breaks[i]; j++)
            {
                arr[j] = cbHandCard[breaks[i] + j];
            }

            // arr集合表示 去除将牌后的手牌根据断点分别放入的牌
            parts[i] = getWei(arr, arr.Length);
        }

        return parts;
    }

因为3n中的牌型的类型不确定。所以此处采取一种拆解法
举例 (五万、五万、五万、六万、七万、七万、八万、八万、九万)
因为此为连续的牌型、所以放入一个集合中、用牌的数量来表示 (3 1 2 2 1)
分析:取出三张牌 (3 1 2)来判断这三张牌中是否有三张相等的、如果有则去除首位 余 1 2 (后两位 2 1)
继续取出三张牌 ( 1 2 2) 三张都大于等于1 递减一 排除顺子 余 1 1 (后一位 1)
剩下三张(1 1 1) 可胡

以上思路代码实现

        /// <summary>
        /// 判断断点处理后的值是否符合规则
        /// </summary>
        /// <param name="cbCardWei"> 断点后经过处理的值 (例如 1 1 2 1 1) </param>
        /// <returns></returns>
        bool isMianPart(int cbCardWei)
        {
            int cache = cbCardWei;
            for (int i = 0; i < 9; i++)
            {
                cache = carryChange(cache);
                if (cache == 0) return true;
            }
            return false;
        }



 int carryChange(int cbCardWei)
           {

            int keyNum = 3;
            int result = cbCardWei;
            int weilength = (int)Mathf.Log10(cbCardWei) + 1;//位的长度

            int weihead = (int)(cbCardWei / Mathf.Pow(10, (weilength - keyNum)));//位的头部

            if (carry(weihead) != 0)
            {
                result = (int)(cbCardWei - (carry(weihead) * Mathf.Pow(10, (weilength - keyNum))));//位运算结果

            }//前三位拆分无结果拆前两位
            else
            {

                keyNum = 2;
                weihead = (int)(cbCardWei / Mathf.Pow(10, (weilength - keyNum)));

                if (carry(weihead) != 0)
                {
                    result = (int)(cbCardWei - (carry(weihead) * Mathf.Pow(10, (weilength - keyNum))));//位运算结果
                }//前两位拆分无结果只拆前一位
                else
                {

                    keyNum = 1;
                    weihead = (int)(cbCardWei / Mathf.Pow(10, (weilength - keyNum)));
                    if (carry(weihead) != 0)
                    {
                        result = (int)(cbCardWei - (carry(weihead) * Mathf.Pow(10, (weilength - keyNum))));//位运算结果
                    }//拆一位无结果则result结果不改变
                }
            }
            return result;
        }





               int carry(int key)
               {//进位
                switch (key)
               {
                case 3: return 3;
                case 4: return 3;

                case 31: return 30;
                case 32: return 30;
                case 33: return 33;
                case 44: return 33;

                case 111: return 111;
                case 112: return 111;
                case 113: return 111;
                case 114: return 114;

                case 122: return 111;
                case 123: return 111;
                case 124: return 111;

                case 133: return 111;
                case 134: return 111;

                case 141: return 141;
                case 142: return 141;
                case 143: return 141;
                case 144: return 144;

                case 222: return 222;
                case 223: return 222;
                case 224: return 222;

                case 233: return 222;
                case 234: return 222;
                case 244: return 222;

                case 311: return 300;
                case 312: return 300;
                case 313: return 300;
                case 314: return 300;

                case 322: return 300;
                case 323: return 300;
                case 324: return 300;

                case 331: return 330;
                case 332: return 330;
                case 333: return 333;
                case 334: return 333;

                case 341: return 330;
                case 342: return 330;
                case 343: return 330;
                case 344: return 333;

                case 411: return 411;
                case 412: return 411;
                case 413: return 411;
                case 414: return 414;

                case 422: return 411;
                case 423: return 411;
                case 424: return 411;

                case 433: return 411;
                case 434: return 411;

                case 441: return 441;
                case 442: return 441;
                case 443: return 441;
                case 444: return 444;

            }
              return 0;
          }



本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

麻将算法之 ------ 胡牌算法 的相关文章

  • 用两台TFmini Plus实现 PPT切换

    1 PPT自动切换模块简介 本模块利用北醒公司两台TFmini Plus Arduino DUE板 一个低功率红外激光指向灯 两个LED指示灯 一个单向开关 一个双向开关和连接线结合设计 系统作用 xff1a 通过检测人的手势 xff0c
  • TFmini-Plus在Arduino上的开发例程

    TFmini Plus在Arduino上的开发例程 本例程以Arduino Uno 和 DUE板作为示例 xff0c 主要帮助客户快速熟悉我公司雷达 xff0c 减少产品的研发周期 关于Arduino的详细介绍请参考以下网站 xff1a 英
  • TFmini 常见问题与解答

    TFmini 是一款小型激光雷达模组 主要实现实时 无接触式的距离测量功能 xff0c 具有测量准确 稳定 高速的特点 TFmini常见问题与解答 Q xff1a 请问TFmini是否可以在水面检测 xff1f A xff1a 不建议在水面
  • TFmini和TFmini-Plus——激光雷达模组 资料汇总

    本文主要介绍TFmini和TFmini Plus激光雷达相关资料 目录 产品基本介绍产品基本FAQ与开源硬件的基本匹配其他 xff08 I C版参考方案 xff09 应用方案 一 产品基本介绍 产品规格书 产品使用说明等有关下载地址 xff
  • 使用数传电台无线远程传输雷达数据的参考方案

    本文介绍使用数传电台无线远程传输雷达数据的参考方案 方案概述 本方案为使用数传电台方式无线远程传输北醒TF系列雷达数据的参考方案 本方案中所使用的除TF激光雷达外的设备和工具并非北醒官方产品 xff0c 不提供任何官方售后服务 xff0c
  • TFmini(Plus)在STM32上的应用

    STM32 TFmini 下载地址 xff1a 链接 https pan baidu com s 15IqhmbFTEHsLFydYbzir w 提取码 8iv1 复制链接打开 本文件夹为TFmini的STM32转接例程程序 xff0c 使
  • 阿里云ubuntu18.04搭建图形界面的介绍

    经过几天的奋战 xff0c 在综合别人的宝贵经验之后 xff0c 终于把阿里云的ubuntu18 04的图形界面搭配出来了 xff0c 在此把我个人的经历分享给大家参考一下 首先声明 我是参考原博主的经验搭建出来的 xff0c 具体链接可以
  • 嵌入式开发Verilog教程(一)——数字信号处理、计算、程序、 算法和硬线逻辑的基本概念

    嵌入式开发Verilog教程 xff08 一 xff09 数字信号处理 计算 程序 算法和硬线逻辑的基本概念 一 引言二 数字信号处理三 计算四 算法与数据结构五 编程语言和程序六 系统结构与硬线逻辑七 设计方法学八 专用硬线逻辑与微处理器
  • IntelliJ IDEA 2020.1 正式发布,15 项重大特性、官方支持中文了! | 原力计划

    头图 amp 作者 YourBatman xff0c CSDN博客专家 责编 唐小引 出品 CSDN xff08 ID xff1a CSDNnews xff09 前言 千呼万唤始出来 xff01 自从官方在 2020 01 20 发布了其
  • 状态观测器

    干扰 xff0c 输出 xff0c 系统内状态都是一种状态 xff0c 状态观测器可以扩展干扰观测器 定义系统实际输出和理论输出的误差d xff0c 对d求导得到一个公式 xff0c 系统理论输出求导得到一个公式 xff0c 组建成二元方程
  • python更新pip失败-解决Python pip 自动更新升级失败的问题

    在使用python pip的时候 xff0c 经常会发生pip版本过低导致失败的情况 xff0c 这时候 xff0c 系统会提示让你使用如下命令对pip进行升级更新 xff1a python m pip install upgrade pi
  • es6小记

    const let关键字 let xff1a 声明 使js存在块级作用域 const xff1a 定义常亮 xff0c 定义以后不可以更改 若是引用的类型 xff0c 那可以更改他的属性 const a 61 b 789 br a b 61
  • js检测浏览器内核、版本号

    js检测浏览器内核 版本号 span class hljs function span class hljs keyword function span span class hljs title isBroswer span span c
  • 点击地图获取经纬度(基于腾旭地图api)

    废话不多说上图 接着代码段 xff08 注意 xff1a 这里的key要换成自己的key xff09 span class hljs doctype lt DOCTYPE html gt span span class hljs tag l
  • dataTable使用

    官网 DataTable英文网址 DataTables中文网址 DataTables一些例子 本文主要是围绕下面的例子进行讲解的 xff0c 请看效果图 说明 xff1a 全选功能 xff0c 点击删除 xff0c 批量删除 xff0c 批
  • 判断checkbox 是否选中

    checkbox 选中事件 首先input的标签设置checkbox lt input name 61 span class hljs string 34 operaMode 34 span span class hljs class sp
  • jQuery添加新元素/内容

    整理了一下jq添加元素 HTML的一些操作 xff0c 效果图见下 结构插入 内部插入 append 在被选元素的结尾插入内容 appendTo 方法在被选元素的结尾 xff08 仍然在内部 xff09 插入指定内容 prepend 在被选
  • 高德地图开发bug

    第一次使用的时候报错 INVALID USER DOMAIN 然后百度了一下 xff0c 官方文档上是这么说的 xff1a xff0c 请先检查一下您在申请Key的时候 xff0c 是否设置了域名白名单 xff0c 如果设定了 xff0c
  • 新 iPhone SE 卖 3299 元起,香不香?

    作者 吴波 来源 网易科技 xff08 ID xff1a tech 163 xff09 4 月 15 日 xff0c 等待了 1485 天之后 xff0c 当年的 4 英寸 性能小钢炮 iPhone SE 终于迎来全新升级款 The New

随机推荐

  • 图片剪裁并预览上传

    预览效果 详细代码讲解 现在有点忙 xff0c 稍后便会讲解 demo下载地址图片剪裁并预览上传
  • js实现下载功能

    js实现下载功能 最近需要做一个下载功能 xff0c 然后到网上查了查 xff0c 顺便写了个小demo xff1b 在h5新特性里面 xff1b a标签有个download的属性 xff1b span class hljs tag lt
  • 前端js免费的CDN网站

    1 BootCDN http www bootcdn cn 目前前端开源的项目几乎都涵盖了 xff0c 支持http和https 2 百度静态资源公共库 http cdn code baidu com 一个是稳定 xff0c 快速 xff0
  • js区号插件(全国电话区号)

    最近由于要和美团和饿了么外卖对接 xff0c 地址要输入区号 xff1b 网上百度没有找到插件 xff1b 于是自己就动手撸了一个 xff1b 效果图见下 xff1b 为什么第一个是浙江呢 xff0c 因为我在杭州啊 xff01 xff01
  • 那些看过的好文章

    我总结的js性能优化的小知识http www cnblogs com liyunhua p 4529086 html19 个 JavaScript 有用的简写技术https segmentfault com a 11900000126738
  • java小白成长记02

    位运算符 左移 lt lt 就是乘以2的次幂运算 xff0c 移几位就是几次幂 xff1b 3 lt lt 3即为3 2 2 2 61 24 xff1b 6 lt lt 2即为6 2 2 61 24 xff1b 右移 gt gt 就是除以2
  • web前端开发-谷歌浏览器插件

    搜集了一些web前端开发所需要的谷歌浏览器插件 xff1b 具体内容见下图 xff1b 下载地址 使用方法 xff1a 点击谷歌浏览器右上角 gt 更多工具 gt 扩展程序 xff1b 直接将 crx文件拖进去即可安装 xff1b
  • webpack4+react+antd从零搭建React脚手架(一)

    本人是在windows环境下搭建的 xff0c ios自行摸索 代码地址PreByter 项目初始化 首先就是项目初始化 xff0c 一定确保安装node或者yarn二者均可 xff0c npm init 或者 yarn init 此时会生
  • webpack4+react+antd从零搭建React脚手架(二)

    接着上文 xff0c 对webpack 的配置的优化和对css xff0c 图片的编译 以及引入antd 项目代码地址react project 优化webpack 生成的文件名添加Hash值 output filename 34 js n
  • 一线大厂面试必知必会大公开

    作者 代码随想录 来源 代码随想录 企业一般通过几轮技术面试来考察大家的各项能力 xff0c 一般流程如下 xff1a 一面机试 xff1a 一般会考选择题和编程题 二面基础算法面 xff1a 就是基础的算法都是该专栏要讲的 三面综合技术面
  • Python实现观测值o文件和精密星历sp3文件读取

    博主之前准备利用Python编写精密单点定位程序 xff0c 奈何写了一半的读取文件代码 xff0c 觉得太浪费时间 xff0c 就此作罢 xff0c 这些时间不如多用来研究现有代码 xff0c 把这部分放弃的代码拿出来 xff0c 希望给
  • webpack4+react+antd从零搭建React脚手架(四)-redux搭建

    redux 文档地址 redux是对数据的状态管理 xff0c 是react不可缺少的一部分 xff0c 具体的概念这里就不进行详细的介绍 本文主要是介绍怎么引入redux和使用redux 单向数据流 xff1a 从父组件流向子组件 xff
  • Python:入门到实践-安装

    Python 入门到实践 安装 安装python环境安装启动终端会话HelloWorld 安装python环境 写在前面 xff0c 本文是基于Windows10系统下学时 xff0c 其他环境下 请自行研究 python版本是3 6 1
  • 阿里云部署web项目

    开始 xff08 额 xff0c 主要是自己忘了 xff0c 别人可以跳过 xff09 修改终端登录密码 xff1a 在实例里面 xff0c 有一个更多 xff0c 来管理密码安装yum xff08 其实也可以用pipe进行安装 xff0c
  • 前端项目部署到阿里云

    由于本人是个前端这里只介绍前端项目的部署 xff08 后台的部署见下一篇 xff09 准备工作 下载两个软件Xshell和Xftp xff08 也可以使用WinSCP 我使用的是windows系统 xff09 购买阿里云 xff0c 看需要
  • 如何重启MySQL,正确启动MySQL

    RedHat Linux Fedora Core Cent OS 1 启动 xff1a etc init d mysqld start 2 停止 xff1a etc init d mysqld stop 3 重启 xff1a etc ini
  • 阿里云Ubuntu16.04 python升级

    Ubuntu16 04 python2 7升级python3 5 正常情况下 xff0c 你安装好ubuntu16 04版本之后 xff0c 系统会自带 python2 7版本 xff0c 如果需要下载新版本的python3 5 xff0c
  • react 脚手架 run eject 之后 打包生成map文件 体积过大

    react 脚手架 打包生成map文件 体积过大 写在前面 xff0c map文件是帮助我们查看报错的位置的 map文件由devtool属性控制 xff0c 然后全文搜索devtool 发现在webpack config js文件 150
  • 机器学习实战分享:用 Python 进行信用卡欺诈检测

    本文旨在使用 XGBoost 随机森林 KNN 逻辑回归 SVM 和决策树解决分类问题 xff0c 内容较长 xff0c 建议收藏 关注 点赞 案例简介 假设你受雇于帮助一家信用卡公司检测潜在的欺诈案件 xff0c 你的工作是确保客户不会因
  • 麻将算法之 ------ 胡牌算法

    麻将数据牌集合 span class hljs keyword private span span class hljs keyword int span cardDataArray 61 span class hljs number 0x