麻将番型计算(二人麻将)

上一篇文章序数了关于使用索引查表法进行麻将胡牌判定,这篇文章,我们将会对胡牌的番型进行计算,这里的番型指的是国标下的麻将番型,文章中的代码,暂时只考虑了二人麻将(只有万牌和字牌),想要通用,可以按着同样的方法进行扩展。

国标二人麻将番型表

88番

番型 胡牌方式 示例
大四喜 胡牌时,牌里有4组风刻子(杠)加一对将牌组成的牌型。(不计门风刻、圈风刻、小四喜、三风刻、碰碰胡、幺九刻) 在这里插入图片描述
大三元 胡牌时,牌里有中、发、白3副刻子。(不计双箭刻、箭刻) 在这里插入图片描述
九莲宝灯 由一种花色序数组成的特定牌型,见同花色任何一张序数牌即成胡牌。不计清一色,门前清,自摸 在这里插入图片描述
大七星 胡牌为七对子,并且由“东南西北中发白“其中字牌构成,不计七对,三元七对,四喜七对,全带幺,单钓将,门前清,自摸,字一色 在这里插入图片描述
四杠 4个杠,不计三杠,双明杠,明杠,单钓将 在这里插入图片描述
连七对 由一种花色序数牌组成序数相连的7个对子的胡牌。不计七对,单钓将,门前清,自摸,清一色 在这里插入图片描述
天胡 庄家在发完牌就胡牌。如果庄家有补花,在补完花后就胡牌也算。如果庄家在发完牌后有暗杠, 那么不算天和,不计边张,坎张单钓将不求人,和绝张,自摸
地胡 闲家摸到第一张牌 就胡牌,称为地和。如果闲家抓的第一张牌是花牌,那么补花之后胡牌也算地和。如果闲家抓牌前有人吃碰杠(包括暗杠),那么不算地和。

64番

番型 胡牌方式 示例
小四喜 胡牌时,牌里有风牌的3副刻子及将牌。不记番:三风刻、幺九刻。 在这里插入图片描述
小三元 胡牌时,牌里有箭牌的两副刻子及将牌。不记番:箭刻、双箭刻。 在这里插入图片描述
字一色 字一色:胡牌时,牌型由字牌的刻子(杠)、将组成。不记番:碰碰和、混么九、全带幺、么九刻。 在这里插入图片描述
四暗刻 胡牌时,牌里有4个暗刻(暗杠)。不记番:门前清、碰碰和、三暗刻、双暗刻、不求人。 在这里插入图片描述
一色双龙会 胡牌时,牌型由一种花色的两个老少副,5为将牌组成。不记番:平和、七对、清色、一般高、老少副。 在这里插入图片描述

48番

番型 胡牌方式 示例
一色四同顺 胡牌时,牌里有一种花色且序数相同的4副顺子。不记番:一色三节高、一般高、四归一,一色三同顺、七对。 在这里插入图片描述
一色四节高 胡牌时牌里有一种花色且序数依次递增一位数的4副刻子(或杠子)。不记番:一色三同顺、一色三节高、碰碰和。 在这里插入图片描述

32番

番型 胡牌方式 示例
一色四步高 胡牌时,牌里有一种花色4副依次递增一位数或依次递增二位数的顺子。不记番:一色三步高。 在这里插入图片描述
三杠 胡牌时,牌里有3副杠,明杠暗杠均可。 在这里插入图片描述
混幺九 胡牌时,由字牌和序数牌、九的刻子及将牌组成的牌型。不记番:碰碰和、幺九刻、全带么。 在这里插入图片描述

24番

番型 胡牌方式 示例
七对 胡牌时,胡牌时,牌型由7个对子组成。(不计门前清、不求人、单钓将) 在这里插入图片描述
清一色 胡牌时,牌型由一种花色的顺序牌组成。 在这里插入图片描述
一色三同顺 胡牌时,牌里有一种花色且依次递增一位数字的3副刻子。不记番: 一色三节高、一般高 在这里插入图片描述
一色三节高 胡牌时,牌里有一种花色且依次递增一位数字的3副刻子。不记番: 一色三同顺 在这里插入图片描述

16番

番型 胡牌方式 示例
清龙 胡牌时,有一种相同花色的123,456,789三付顺子即可。清龙就是清一色条龙。不记番:连6、老少副。 在这里插入图片描述
一色三步高 胡牌时,牌里有一种花色的牌,依次递增一位或依次递增二位数字的3副顺子。三暗刻:胡牌时,牌里有3个暗刻。不记番:双暗刻。 在这里插入图片描述
天听 庄家打出第-张牌时报听称为天听;发完牌后闲家便报听也称为天听。天听要在胡牌后才算番。如果庄家在发完牌后有暗杠,则庄家不算天听。如果发完牌之后有补花,补花之后报听也算天听。
三暗刻 胡牌时,牌里有3个暗刻。 在这里插入图片描述

12番

番型 胡牌方式 示例
大于5 胡牌时,牌型由序数牌6 9的顺子、刻子、将牌组成。 在这里插入图片描述
小于5 胡牌时,牌型由序数牌1-4的顺子、刻子、将牌组成。 在这里插入图片描述
三风刻 胡牌时,牌里有3个风刻。 在这里插入图片描述

8番

番型 胡牌方式 示例
妙手回春 自摸牌墙上最后一张牌胡牌。不记番: 自摸。
海底捞月 和打出的最后一张牌。
杠上开花 胡牌时,开杠抓进的牌成胡牌。不记番:自摸。
抢杠胡 胡牌时,和别人自抓开明杠的牌。不记番:胡绝张。

6番

番型 胡牌方式 示例
碰碰胡 胡牌时,牌型由4副刻子(或杠)、将牌组成。 在这里插入图片描述
双暗杠 胡牌时,有2个暗杠。 在这里插入图片描述
混一色 胡牌时,牌型由一种花色序数牌及字牌组成。 在这里插入图片描述
全求人 胡牌时,全靠吃牌、碰牌、单钓别人打出的牌胡牌。不记番:单钓。 在这里插入图片描述
双箭刻 胡牌时,牌里有2副箭刻(或杠)。不记番:箭刻。 在这里插入图片描述

4番

番型 胡牌方式 示例
全带幺 胡牌时,每副牌、将牌都有么牌。(胡牌时各组牌除了字牌都必须有一或九的序数牌)。 在这里插入图片描述
不求人 胡牌时,4副牌及将中没有吃牌、碰牌(包括明杠),自摸胡牌。
双明杠 胡牌时,牌里有2个明杠。不记番:明杠。 在这里插入图片描述
和绝张 胡牌时,胡牌池、桌面已亮明的3张牌所剩的第4张牌。

2番

番型 胡牌方式 示例
箭刻 胡牌时,牌里有中、发、白,这3个牌中的任一个牌组成的1副刻子。 在这里插入图片描述
门风刻 胡牌时牌里有与门风相同的风刻。
门前清 没有吃、碰、明杠,和别人打出的牌。
平胡 胡牌时,牌型由4副顺子及序数牌作将组成。边、坎、钓不影响平和。
四归一 胡牌时,牌里有4张相同的牌归于一家的顺、刻子、对、将牌中(不包括杠牌)。双暗刻:胡牌时,牌里有2个暗刻。 在这里插入图片描述
双暗刻 胡牌时,牌里有2个暗刻。
暗杠 胡牌时,牌里有一副自抓4张相同的牌且开杠。
断幺 胡牌时,牌里没有一、九牌及字牌。 在这里插入图片描述
报听 报听后胡牌。

1番

番型 胡牌方式 示例
一般高 胡牌时,牌里有一种花色且序数相同的2副顺子。 在这里插入图片描述
连六 一种花色六张序数相连的顺子(例如: 3-4-5条和6-7-8条) 在这里插入图片描述
老少副 胡牌时,牌里花色相同的123、789的顺子各一副。 在这里插入图片描述
幺九刻 胡牌时,牌里有序数为一、九的一副刻子(杠)或是字牌的一副刻子(杠)。 在这里插入图片描述
明杠 自己有暗刻,碰别人打出的一张相同的牌开杠:或自己抓进一张与碰的明刻相同。 在这里插入图片描述
边张 单和123的3及789的7或1233和3、7789和7都为边张。手中有12345和3,56789和7不算边张。 在这里插入图片描述
坎张 胡牌时,和2张牌之间的牌。4556和5也为坎张,手中有45567和6不算坎张。 在这里插入图片描述
单钓将 钓单张牌作将成和。 在这里插入图片描述
自摸 自己抓进牌成胡牌。
二五八将 胡牌时,将牌是二万、五万、八万。

番型计算

在上一篇文章中提到的查表法进行胡牌判定,其中查表法会给我们提供两个返回值,一个是是否胡牌的布尔值,还有一个是胡牌类型返回结果(MahjongResult):

//牌型结果
type MahjongResult struct {
	Num_ke     int    //刻子数量
	Num_shun   int    //顺子数量
	Jiang      byte   //将牌值
	Array_ke   []byte //刻子数组
	Array_shun []byte //顺子数组
	Qidui      bool   //是否七对
	Tongtian   bool   //是否通天
}

右手牌(吃碰杠):

//一组牌
type Meld struct {
	Key    byte       //Key牌 牌面值
	Kind   WEAVE_KIND //组牌类型
	Ids    []byte     //id 数组 唯一id
	Values []byte     //牌面值 数组
	Sites  []byte     //座位 数组
}

结合右手牌(吃碰杠)信息,将每一种胡牌牌型统计成一个结构体:

//统计一种胡牌类型麻将角色(刻子等)
type MahjongCount struct {
	Jiang        byte   //将牌值
	Array_ke     []byte //刻子数组 只列第一个 索引(包含暗刻 和碰)
	Array_a_ke   []byte //暗刻
	Array_shun   []byte //顺子数组 只列第一个
	Array_c_shun []byte //吃牌获得的顺子
	Array_h_shun []byte //手牌中的顺子
	Array_gang   []byte //杠数组 只列第一个 (包含明杠 暗杠)
	Array_m_gang []byte //明杠
	Array_a_gang []byte //暗杠
	TileIndexs   []byte //玩家所有牌  索引
	QiDui        bool   //是否七对
	TongTian     bool   //是否通天
	Zimo         bool   //	是否自摸
	HuId         byte   //	胡牌那张 唯一id
	HuIndex      byte   //胡得那张牌得索引
	MenFeng      byte   //当前胡牌玩家门风(庄为0x31东 闲家为 033西)
	TingBool     bool   //是否报听
}

转换代码:

//将麻将 手牌 和右手牌统一到MahjongCount,每一个MahjongCount都是一种胡牌牌型
func CountMahjongResult(results []*MahjongResult, meld []*Meld, tileIndexs []byte, zimo bool, huId, huIndex, menFeng byte, baoTing bool) []*MahjongCount {
	res := make([]*MahjongCount, 0)
	if results != nil && len(results) > 0 {
		//遍历手牌
		for _, v := range results {
			mc := &MahjongCount{}
			mc.Zimo = zimo
			mc.HuId = huId
			mc.HuIndex = huIndex
			mc.TingBool = baoTing
			mc.TileIndexs = make([]byte, len(tileIndexs))          //初始化tiles
			mc.TileIndexs = append(mc.TileIndexs, tileIndexs...)                          //将tilesIndexs值复制给 tiles
			mc.QiDui = v.Qidui                                     //是否七对
			mc.TongTian = v.Tongtian                               //是否通天
			mc.Jiang = v.Jiang                                     //将牌
			mc.MenFeng = menFeng                                   //门风
			mc.Array_ke = append(mc.Array_ke, v.Array_ke...)       //暗刻
			mc.Array_a_ke = append(mc.Array_a_ke, v.Array_ke...)   //暗刻
			mc.Array_shun = append(mc.Array_shun, v.Array_shun...) //顺子
			mc.Array_h_shun = append(mc.Array_h_shun, v.Array_shun...)
			res = append(res, mc)
		}
	}
	//遍历右手牌
	if meld != nil && len(meld) > 0 {
		for _, m := range meld {
			index := ValueToIndex(m.Values[0])

			if len(res) == 0 {
				mc := &MahjongCount{}
				mc.Zimo = zimo
				mc.HuId = huId
				mc.HuIndex = huIndex
				mc.MenFeng = menFeng //门风
				mc.TingBool = baoTing
				mc.TileIndexs = make([]byte, len(tileIndexs)) //初始化tiles
				mc.TileIndexs = append(mc.TileIndexs, tileIndexs...)                 //将tilesIndexs值复制给 tiles
				res = append(res, mc)
			}
			for _, v := range res {

				//右手 牌类型为左吃 或右吃或中吃
				if m.Kind == KIND_LEFT || m.Kind == KIND_RIGHT || m.Kind == KIND_CENTER {
					v.Array_shun = append(v.Array_shun, index) //将顺子添加到count中
					v.Array_c_shun = append(v.Array_c_shun, index)
					for _, m := range m.Values {
						v.TileIndexs[ValueToIndex(m)] ++
					}
				} else if m.Kind == KIND_PENG { //如果右手牌是 刻子
					v.Array_ke = append(v.Array_ke, index)
					v.TileIndexs[index] += 3
				} else if m.Kind == KIND_GANG || m.Kind == KIND_ANGANG || m.Kind == KIND_JIAGANG { //如果右手牌是 明杠或者暗杠
					v.Array_gang = append(v.Array_gang, index)
					//将右手牌加入到所有牌中
					v.TileIndexs[index] += 4
					if m.Kind == KIND_ANGANG { //暗杠
						v.Array_a_gang = append(v.Array_a_gang, index)
					} else { //明杠
						v.Array_m_gang = append(v.Array_m_gang, index)
					}
				}
			}
		}
	}
	for _, v := range res {
		sortByte(v.Array_ke)
		sortByte(v.Array_gang)
		sortByte(v.Array_shun)
		sortByte(v.Array_m_gang)
		sortByte(v.Array_a_ke)
		sortByte(v.Array_a_gang)
		sortByte(v.Array_c_shun)
	}
	return res
}

//升序排序
func sortByte(a []byte) {
	for i := 0; i < len(a); i++ {
		l := len(a)
		for j := i + 1; j < l; j++ {
			if a[i] > a[j] {
				temp := a[j]
				a[j] = a[i]
				a[i] = temp
			}
		}
		l--
	}
}

进过统计之后的牌型,只需要遍历所有番型进行判断即可。
由于番型种类过多,所以这里我们将函数写进map中,通过遍历这个map对其进行判断。番型返回的结果是一个二进制掩码,这里的番型有62种,所以我们可以使用int64位数对番型进行表示,一位二进制代表着一种番型。同时再使用一种排除番型,表示不能胡某种番。番型二进制掩码表示如下:

package mahjong

//胡牌掩码 null表示没有胡牌(试用二进制表示 1位代表胡牌)
const NULL = int64(0)
const (
	// 二五八将 1
	ERWUBAJIANG = int64(1) << iota
	// 自摸 10
	ZIMO
	// 单调将 100
	DANDIAOJIANG
	// 坎张 1000
	KANZHANG
	// 边张 10000
	BIANZHANG
	// 明杠100000
	MINGGANG
	// 幺九刻 1000000
	YAOJIUKE
	// 老少副 10000000
	LAOSHAOFU
	// 连六 100000000
	LIANLIU
	// 一般高 1000000000
	YIBANGAO
	// 2f番
	// 报听 10000000000
	BAOTING
	// 断幺 100000000000
	DUANYAO
	// 暗杠 13 2
	ANGANG
	// 双暗刻 14 2
	SHUANGANKE
	// 四归一 15 2
	SIGUIYI
	// 平胡 16 2
	PINGHU
	// 门前清 17 2
	MENQIANQING
	// 门风刻 18 2
	MENFENGKE
	// 箭刻 19 2
	JIANKE
	// 4fan
	// 和绝张 20 4
	HUJUEZHANG
	// 双明杠 21 4
	SHUANGMINGGANG
	// 不求人 22 4
	BUQIUREN
	// 全带幺 23 4
	QUANDAIYAO
	// 6fan
	// 双箭刻 24 6
	SHUANGJIANKE
	// 全求人 25 6
	QUANQIUREN
	// 混一色 26 6
	HUNYISE
	// 双暗杠 27 6
	SHUANGANGANG
	// 碰碰胡 28 6
	PENGPENGHU
	// 8fan
	// 抢杠胡 29 8
	QIANGGANGHU
	// 杠上开花 30 8
	GANGSHANGKAIHUA
	// 海底捞月 31 8
	HAIDILAOYUE
	// 妙手回春 32 8
	MIAOSHOUHUICHUN
	// 12fan
	// 三风刻 33 12
	SANFENGKE
	// 小于五 34 12
	XIAOYUWU
	// 大于五 35 12
	DAYUWU
	// 16fan
	// 三暗刻 36 16
	SANANKE
	// 天听 37 16
	TIANTING
	// 一色三步高 38 16
	YISESANBUGAO
	// 清龙 39 16
	QINGLONG
	// 24fan
	// 一色三节高 40 24
	YISESANJIEGAO
	// 一色三同顺 41 24
	YISESANTONGSHUN
	// 清一色 42 24
	QINGYISE
	// 七对 43 24
	QIDUI
	// 32fan
	// 混幺九 44 32
	HUNYAOJIU
	// 三杠 45 32
	SANGANG
	// 一色四步高 46 32
	YISESIBUGAO
	// 48fan
	// 一色四节高 47 48
	YISESIJIEGAO
	// 一色四同顺 48 48
	YISESITONGSHUN
	// 64fan
	// 一色双龙会 49 64
	YISESHUANGLONGHUI
	// 四暗刻 50 64
	SIANKE
	// 字一色 51 64
	ZIYISE
	// 小三元 52 64
	XIAOSANYUAN
	// 小四喜 53 64
	XIAOSIXI

	// 88fan
	// 地胡 55 88
	DIHU
	// 天胡 56 88
	TIANHU
	// 连七对 57 88
	LIANQIDUI
	// 四杠 58 88
	SIGANG
	// 大七星 59 88
	DAQIXING
	// 九宝莲灯 60 88
	JIUBAOLIANDENG
	// 大三元 61 88
	DASANYUAN
	// 大四喜 62 88
	DASIXI
)

//胡牌番型判断切片
var HuType_slice = []int64 {
	DASIXI,
	DASANYUAN,
	JIUBAOLIANDENG,
	DAQIXING,
	SIGANG,
	LIANQIDUI,
	XIAOSIXI,
	XIAOSANYUAN,
	ZIYISE,
	SIANKE,
	YISESHUANGLONGHUI,
	YISESITONGSHUN,
	YISESIJIEGAO,
	YISESIBUGAO,
	SANGANG,
	HUNYAOJIU,
	QIDUI,
	QINGYISE,
	YISESANTONGSHUN,
	YISESANJIEGAO,
	QINGLONG,
	YISESANBUGAO,
	TIANTING,
	SANANKE,
	DAYUWU,
	XIAOYUWU,
	SANFENGKE,
	MIAOSHOUHUICHUN,
	HAIDILAOYUE,
	GANGSHANGKAIHUA,
	PENGPENGHU,
	SHUANGANGANG,
	HUNYISE,
	QUANQIUREN,
	SHUANGJIANKE,
	QUANDAIYAO,
	BUQIUREN,
	HUJUEZHANG,
	JIANKE,
	MENFENGKE,
	MENQIANQING,
	PINGHU,
	SIGUIYI,
	SHUANGANKE,
	ANGANG,
	DUANYAO,
	BAOTING,
	YIBANGAO,
	LIANLIU,
	LAOSHAOFU,
	YAOJIUKE,
	MINGGANG,
	BIANZHANG,
	KANZHANG,
	DANDIAOJIANG,
	ZIMO,
	ERWUBAJIANG,
}

//胡牌掩码 对应的胡牌名称
var HuType_name = map[int64]string{
	NULL:              "NULL",
	ERWUBAJIANG:       "ERWUBAJIANG",
	ZIMO:              "ZIMO",
	DANDIAOJIANG:      "DANDIAOJIANG",
	KANZHANG:          "KANZHANG",
	BIANZHANG:         "BIANZHANG",
	MINGGANG:          "MINGGANG",
	YAOJIUKE:          "YAOJIUKE",
	LAOSHAOFU:         "LAOSHAOFU",
	LIANLIU:           "LIANLIU",
	YIBANGAO:          "YIBANGAO",
	BAOTING:           "BAOTING",
	DUANYAO:           "DUANYAO",
	ANGANG:            "ANGANG",
	SHUANGANKE:        "SHUANGANKE",
	SIGUIYI:           "SIGUIYI",
	PINGHU:            "PINGHU",
	MENQIANQING:       "MENQIANQING",
	MENFENGKE:         "MENFENGKE",
	JIANKE:            "JIANKE",
	HUJUEZHANG:        "HUJUEZHANG",
	SHUANGMINGGANG:    "SHUANGMINGGANG",
	BUQIUREN:          "BUQIUREN",
	QUANDAIYAO:        "QUANDAIYAO",
	SHUANGJIANKE:      "SHUANGJIANKE",
	QUANQIUREN:        "QUANQIUREN",
	HUNYISE:           "HUNYISE",
	SHUANGANGANG:      "SHUANGANGANG",
	PENGPENGHU:        "PENGPENGHU",
	QIANGGANGHU:       "QIANGGANGHU",
	GANGSHANGKAIHUA:   "GANGSHANGKAIHUA",
	HAIDILAOYUE:       "HAIDILAOYUE",
	MIAOSHOUHUICHUN:   "MIAOSHOUHUICHUN",
	SANFENGKE:         "SANFENGKE",
	XIAOYUWU:          "XIAOYUWU",
	DAYUWU:            "DAYUWU",
	SANANKE:           "SANANKE",
	TIANTING:          "TIANTING",
	YISESANBUGAO:      "YISESANBUGAO",
	QINGLONG:          "QINGLONG",
	YISESANJIEGAO:     "YISESANJIEGAO",
	YISESANTONGSHUN:   "YISESANTONGSHUN",
	QINGYISE:          "QINGYISE",
	QIDUI:             "QIDUI",
	HUNYAOJIU:         "HUNYAOJIU",
	SANGANG:           "SANGANG",
	YISESIBUGAO:       "YISESIBUGAO",
	YISESIJIEGAO:      "YISESIJIEGAO",
	YISESITONGSHUN:    "YISESITONGSHUN",
	YISESHUANGLONGHUI: "YISESHUANGLONGHUI",
	SIANKE:            "SIANKE",
	ZIYISE:            "ZIYISE",
	XIAOSANYUAN:       "XIAOSANYUAN",
	XIAOSIXI:          "XIAOSIXI",
	DIHU:              "DIHU",
	TIANHU:            "TIANHU",
	LIANQIDUI:         "LIANQIDUI",
	SIGANG:            "SIGANG",
	DAQIXING:          "DAQIXING",
	JIUBAOLIANDENG:    "JIUBAOLIANDENG",
	DASANYUAN:         "DASANYUAN",
	DASIXI:            "DASIXI",
}

//胡牌对应的番值
var HuType_Fan_Value = map[string]int32{
	"NULL":              0,
	"ERWUBAJIANG":       1,
	"ZIMO":              1,
	"DANDIAOJIANG":      1,
	"KANZHANG":          1,
	"BIANZHANG":         1,
	"MINGGANG":          1,
	"YAOJIUKE":          1,
	"LAOSHAOFU":         1,
	"LIANLIU":           1,
	"YIBANGAO":          1,
	"BAOTING":           2,
	"DUANYAO":           2,
	"ANGANG":            2,
	"SHUANGANKE":        2,
	"SIGUIYI":           2,
	"PINGHU":            2,
	"MENQIANQING":       2,
	"MENFENGKE":         2,
	"JIANKE":            2,
	"HUJUEZHANG":        4,
	"SHUANGMINGGANG":    4,
	"BUQIUREN":          4,
	"QUANDAIYAO":        4,
	"SHUANGJIANKE":      6,
	"QUANQIUREN":        6,
	"HUNYISE":           6,
	"SHUANGANGANG":      6,
	"PENGPENGHU":        6,
	"QIANGGANGHU":       8,
	"GANGSHANGKAIHUA":   8,
	"HAIDILAOYUE":       8,
	"MIAOSHOUHUICHUN":   8,
	"SANFENGKE":         12,
	"XIAOYUWU":          12,
	"DAYUWU":            12,
	"SANANKE":           16,
	"TIANTING":          16,
	"YISESANBUGAO":      16,
	"QINGLONG":          16,
	"YISESANJIEGAO":     24,
	"YISESANTONGSHUN":   24,
	"QINGYISE":          24,
	"QIDUI":             24,
	"HUNYAOJIU":         32,
	"SANGANG":           32,
	"YISESIBUGAO":       32,
	"YISESIJIEGAO":      48,
	"YISESITONGSHUN":    48,
	"YISESHUANGLONGHUI": 64,
	"SIANKE":            64,
	"ZIYISE":            64,
	"XIAOSANYUAN":       64,
	"XIAOSIXI":          64,
	"DIHU":              88,
	"TIANHU":            88,
	"LIANQIDUI":         88,
	"SIGANG":            88,
	"DAQIXING":          88,
	"JIUBAOLIANDENG":    88,
	"DASANYUAN":         88,
	"DASIXI":            88,
}

番型掩码确定后就可以定义函数,

//判定胡牌类型 函数集合 key值为胡牌类型掩码,value为func
var funcFan map[int64]func(countResults *mahjong.MahjongCount, mask, noMask *int64)

胡牌番型判定只需遍历这个函数:

//计算胡牌 类型
func CountHuFan(countResults []*mahjong.MahjongCount) (int64, int64) {
	mask := make([]int64, len(countResults))   //胡牌掩码
	noMask := make([]int64, len(countResults)) //不能胡牌型 掩码
	//遍历所有类型
	maxMask := int64(0)
	maxNoMask := int64(0)
	for k, v := range countResults { //遍历结果
		for _, i := range mahjong.HuType_slice {
			if noMask[k]&i > 0 { //判断当前牌型是否可以胡
				continue
			}
			f, ok := funcFan[i]
			if !ok {
				//fmt.Println("没有此牌型:", i)
				continue
			}
			f(v, &mask[k], &noMask[k]) //调用对应函数 判断是否是此类型胡牌
		}
		if mask[k] > maxMask {
			maxMask = mask[k]
			maxNoMask = noMask[k]
		}
	}
	return maxMask, maxNoMask
}

番型函数的map初始化需要在init函数中进行,

//初始化
func init() {
	funcFan = make(map[int64]func(countResults *mahjong.MahjongCount, mask, noMask *int64), len(mahjong.HuType_slice))
	funcFan[mahjong.DASIXI] = IsBigFourHappy                 //大四喜
	funcFan[mahjong.DASANYUAN] = IsDaSanYuan                 //大三元
	funcFan[mahjong.JIUBAOLIANDENG] = IsJiuLianBaoDeng       //九莲宝灯
	funcFan[mahjong.DAQIXING] = IsDaQiXing                   //大七星
	funcFan[mahjong.SIGANG] = IsSiGang                       //四杠
	funcFan[mahjong.LIANQIDUI] = IsLianQiDui                 //连七对
	funcFan[mahjong.XIAOSIXI] = IsXiaoSiXi                   //小四喜
	funcFan[mahjong.XIAOSANYUAN] = IsXiaoSanYuan             //小三元
	funcFan[mahjong.ZIYISE] = IsZiYiSe                       //字一色
	funcFan[mahjong.SIANKE] = IsSiAnKe                       //四暗刻
	funcFan[mahjong.YISESHUANGLONGHUI] = IsYiSeShuangLongHui //一色双龙会
	funcFan[mahjong.YISESITONGSHUN] = IsYiSeSiTongShun       //一色四同顺
	funcFan[mahjong.YISESIJIEGAO] = IsYiSeSiJieGao           //一色四节高
	funcFan[mahjong.YISESIBUGAO] = IsYiSeSiBuGao             //一色四步高
	funcFan[mahjong.SANGANG] = IsSanGang                     //三杠
	funcFan[mahjong.HUNYAOJIU] = IsHunYaoJiu                 //混幺九
	funcFan[mahjong.QIDUI] = IsQiDui                         //七对
	funcFan[mahjong.QINGYISE] = IsQingYiSe                   //清一色
	funcFan[mahjong.YISESANTONGSHUN] = IsYiSeSanTongShun     //一色三同顺
	funcFan[mahjong.YISESANJIEGAO] = IsYiSeSanJieGao         //一色三节高
	funcFan[mahjong.QINGLONG] = IsQingLong                   //清龙
	funcFan[mahjong.YISESANBUGAO] = IsYiSeSanBuGao           //一色三步高
	funcFan[mahjong.TIANTING] = IsTianTing                   //天听
	funcFan[mahjong.SANANKE] = IsSanAnKe                     //三暗刻
	funcFan[mahjong.DAYUWU] = IsDaYuWu                       //大于五
	funcFan[mahjong.XIAOYUWU] = IsXiaoYuWu                   //小于五
	funcFan[mahjong.SANFENGKE] = IsSanFengKe                 //三风刻
	funcFan[mahjong.MIAOSHOUHUICHUN] = IsMiaoShouHuiChun     //妙手回春
	funcFan[mahjong.HAIDILAOYUE] = IsHaiDiLaoYue             //海底捞月
	funcFan[mahjong.GANGSHANGKAIHUA] = IsGangShangKaiHua     //杠上开花	上层方法计算
	funcFan[mahjong.QIANGGANGHU] = IsQiangGangHu             //抢杠胡	上层方法计算
	funcFan[mahjong.PENGPENGHU] = IsPengPengHu               //碰碰胡
	funcFan[mahjong.SHUANGANGANG] = IsShuangAnGang           //双暗杠
	funcFan[mahjong.HUNYISE] = IsHunYiSe                     //混一色
	funcFan[mahjong.QUANQIUREN] = IsQuanQiuRen               //全求人
	funcFan[mahjong.SHUANGJIANKE] = IsShuangJianKe           //双箭刻
	funcFan[mahjong.QUANDAIYAO] = IsQuanDaiYao               //全带幺
	funcFan[mahjong.BUQIUREN] = IsBuQiuRen                   //不求人
	funcFan[mahjong.SHUANGMINGGANG] = IsShuangMingGang       //双明杠
	funcFan[mahjong.HUJUEZHANG] = IsHuJueZhang               //胡绝张
	funcFan[mahjong.JIANKE] = IsJianKe                       //箭刻
	funcFan[mahjong.MENFENGKE] = IsMenFengKe                 //门风刻
	funcFan[mahjong.MENQIANQING] = IsMenQianQing             //门前清
	funcFan[mahjong.PINGHU] = IsPingHu                       //平胡
	funcFan[mahjong.SIGUIYI] = IsSiGuiYi                     //四归一
	funcFan[mahjong.SHUANGANKE] = IsShuangAnKe               //双暗刻
	funcFan[mahjong.ANGANG] = IsAnGang                       //暗杠
	funcFan[mahjong.DUANYAO] = IsDuanYao                     //断幺
	funcFan[mahjong.BAOTING] = IsBaoTing                     //报听
	funcFan[mahjong.YIBANGAO] = IsYiBanGao                   //一般高
	funcFan[mahjong.LIANLIU] = IsLianLiu                     //连六
	funcFan[mahjong.LAOSHAOFU] = IsLaoShaoFu                 //老少副
	funcFan[mahjong.YAOJIUKE] = IsYaoJiuKe                   //幺九刻
	funcFan[mahjong.MINGGANG] = IsMingGang                   //明杠
	funcFan[mahjong.BIANZHANG] = IsBianZhang                 //边张
	funcFan[mahjong.KANZHANG] = IsKanZhang                   //坎张
	funcFan[mahjong.DANDIAOJIANG] = IsDanDiaoJiang           //单钓将
	funcFan[mahjong.ZIMO] = IsZiMo                           //自摸
	funcFan[mahjong.ERWUBAJIANG] = IsErWuBaJiang             //二五八将
}

下面是62种番型计算方法:

/**
0 1 2 3 4 5 6 7 8 9  万 索引
27 28 29 30 风
31 32 33 中发白
 */

/**
params:
	results : 判断手牌是否胡牌返回结果
	melds	:右手牌
 */

//大四喜
//胡牌时,牌里有4组风牌刻子(杠)加一对将牌组成的牌型。(不计门风刻、圈风刻、小四喜、三风刻、碰碰胡、幺九刻)
func IsBigFourHappy(v *mahjong.MahjongCount, mask, noMask *int64) {
	//判断4组风刻子情况
	//判断手牌中的刻子的情况

	num_ke := len(v.Array_ke)
	num_gang := len(v.Array_gang)
	//一副牌中 刻的数量没有4个 并且 杠的数量也没有4个 必定不是大四喜
	if num_ke != 4 && num_gang != 4 {
		return
	} else {
		//杠或刻子的数量是 4个 ,判断是否是风牌杠
		arr := v.Array_ke
		if num_gang == 4 {
			arr = v.Array_gang
		}
		bo := true
		for _, m := range arr {
			if m < 27 || m > 30 { //杠、刻不是风
				bo = false
				break
			}
		}
		if !bo {
			return
		}
		*mask |= mahjong.DASIXI
		//不计门风刻 小四喜 三风刻 碰碰胡 幺九刻
		*noMask |= mahjong.MENFENGKE | mahjong.XIAOSIXI | mahjong.SANFENGKE | mahjong.PENGPENGHU | mahjong.YAOJIUKE
		return
	}

	return
}

//大三元
//胡牌时,牌里有中、发、白3副刻子。(不计双箭刻、箭刻)
func IsDaSanYuan(v *mahjong.MahjongCount, mask, noMask *int64) {

	lenKe := len(v.Array_ke)
	if lenKe < 3 { // 判断胡牌中 刻子数是否大于三 最多只有4刻子
		return
	}
	//如果有三幅以上刻子  则判断这三幅刻子是否是中 发 白 0x35 0x36 0x37
	//如果刻子数=3,并且第一个不是 中 0x35 或者 刻子数=4,并且第二个刻子不是中0x35
	if (lenKe == 3 && mahjong.IndexToValue(v.Array_ke[0]) != 0x35) || (lenKe == 4 && mahjong.IndexToValue(v.Array_ke[1]) != 0x35) {
		return
	}
	*mask |= mahjong.DASANYUAN
	//排除牌型
	*noMask |= mahjong.SHUANGJIANKE | mahjong.JIANKE

	return
}

//九莲宝灯
//由一种花色序数组成的特定牌型,见同花色任何一张序数牌即成胡牌。不计清一色,门前清,自摸
//牌型为 11123456789999
func IsJiuLianBaoDeng(v *mahjong.MahjongCount, mask, noMask *int64) {

	fmt.Println("jiang:", v.Jiang)
	//判断将牌是否是1
	if v.Jiang != 0 {
		return
	}
	//判断刻子是否是9
	if len(v.Array_ke) != 1 || (v.Array_ke[0] != 8) {
		return
	}
	//判断顺子是否是 1 4 7
	if len(v.Array_shun) != 3 || (v.Array_shun[0] != 0 || v.Array_shun[1] != 3 || v.Array_shun[2] != 6) {
		return
	}
	*mask |= mahjong.JIUBAOLIANDENG
	*noMask |= mahjong.QINGYISE | mahjong.MENQIANQING | mahjong.ZIMO

}

//大七星
//胡牌为七对子,并且由“东南西北中发白“其中字牌构成,不计七对,三元七对,四喜七对,全带幺,单钓将,门前清,自摸,字一色
func IsDaQiXing(v *mahjong.MahjongCount, mask, noMask *int64) {

	if !v.QiDui { //如果不是七对
		return
	}
	//遍历所有牌型,可以从东南西北中发白 处开始遍历
	bo := true
	for i := byte(27); i < mahjong.MAX_INDEX; i++ {
		if v.TileIndexs[i] != 2 {
			bo = false
			break
		}
	}
	if bo {
		*mask |= mahjong.DAQIXING
		*noMask |= mahjong.QIDUI | mahjong.QUANDAIYAO | mahjong.DANDIAOJIANG | mahjong.MENQIANQING | mahjong.ZIMO | mahjong.ZIYISE
	}

}

//四杠
//4个杠,不计三杠,双明杠,明杠,单钓将
func IsSiGang(v *mahjong.MahjongCount, mask, noMask *int64) {

	if len(v.Array_gang) != 4 { //杠的数量没有四个
		return
	}
	*mask |= mahjong.SIGANG
	*noMask |= mahjong.SANGANG | mahjong.SHUANGMINGGANG | mahjong.MINGGANG | mahjong.DANDIAOJIANG

}

//连七对
//由一种花色序数牌组成序数相连的7个对子的胡牌。不计七对,单钓将,门前清,自摸,清一色
func IsLianQiDui(v *mahjong.MahjongCount, mask, noMask *int64) {

	if !v.QiDui { //不是七对
		return
	}
	//判断这七对是否是顺序
	bo := true
	for i := byte(0); i < 9; i++ { //由于是二人麻将,只有一种序数牌(万),所以只需判断前九个
		if v.TileIndexs[i] != 2 {
			bo = false
			break
		}
	}
	if bo {
		*mask |= mahjong.LIANQIDUI
		*noMask |= mahjong.QIDUI | mahjong.DANDIAOJIANG | mahjong.MENQIANQING | mahjong.QINGYISE
	}

}

//小四喜
//胡牌时,牌里有风牌的3副刻子及将牌。不记番:三风刻、幺九刻。
func IsXiaoSiXi(v *mahjong.MahjongCount, mask, noMask *int64) {

	lke := len(v.Array_ke)
	fmt.Println(v)
	if lke < 3 { //判断刻子数是否小于三
		return
	}
	//判断将牌是否是风牌
	if v.Jiang > 30 || v.Jiang < 27 {
		return
	}
	num := 0
	//判断是否有三个风牌刻子
	for _, m := range v.Array_ke {
		if m >= 27 && m <= 30 {
			num ++
		}
	}
	if num != 3 {
		return
	}
	*mask |= mahjong.XIAOSIXI
	*noMask |= mahjong.SANFENGKE | mahjong.YAOJIUKE

}

//小三元
//胡牌时,牌里有箭牌的两副刻子及将牌。不记番:箭刻、双箭刻
func IsXiaoSanYuan(v *mahjong.MahjongCount, mask, noMask *int64) {

	//将牌必须是箭牌
	if v.Jiang < 31 {
		return
	}
	//刻子数不能少于2副
	if len(v.Array_ke) <= 1 {
		return
	}
	//刻子中有两副是箭牌
	num := 0
	for _, m := range v.Array_ke {
		if m >= 31 {
			num++
		}
	}
	if num < 2 {
		return
	}
	*mask |= mahjong.XIAOSANYUAN
	*noMask |= mahjong.JIANKE | mahjong.SHUANGJIANKE

}

//字一色(字牌 包含风牌(东南西北)和箭牌(中发白))
//胡牌时,牌型由字牌的刻子(杠)、将组成。不记番:碰碰和、混么九、全带幺、么九刻。
func IsZiYiSe(v *mahjong.MahjongCount, mask, noMask *int64) {

	//将牌必须是字牌
	if v.Jiang < 27 {
		return
	}
	//不可能有顺子
	if len(v.Array_shun) > 0 {
		return
	}
	bo := true
	kg := make([]byte, 0)
	kg = append(append(kg, v.Array_ke...), v.Array_gang...)
	//刻子、杠都要为字牌
	for _, m := range kg {
		if m < 27 {
			bo = false
			break
		}
	}
	if !bo {
		return
	}
	*mask |= mahjong.ZIYISE
	*noMask |= mahjong.PENGPENGHU | mahjong.HUNYAOJIU | mahjong.QUANDAIYAO | mahjong.YAOJIUKE
}

//四暗刻
//胡牌时,牌里有4个暗刻(暗杠)。不记番:门前清、碰碰和、三暗刻、双暗刻、不求人。
func IsSiAnKe(v *mahjong.MahjongCount, mask, noMask *int64) {

	//判断暗刻和暗杠数量之和
	if len(v.Array_a_ke)+len(v.Array_a_gang) < 4 {
		return
	}
	*mask |= mahjong.SIANKE
	*noMask |= mahjong.MENQIANQING | mahjong.PENGPENGHU | mahjong.SANANKE | mahjong.SHUANGANKE | mahjong.BUQIUREN
}

//一色双龙会
//胡牌时,牌型由一种花色的两个老少副,5为将牌组成。不记番:平和、七对、清色、一般高、老少副
//老少副:胡牌时,牌里花色相同的123、789的顺子各一副
//二人麻将只有一种情况 1 2 3 1 2 3 5 7 8 9 7 8 9  5
func IsYiSeShuangLongHui(v *mahjong.MahjongCount, mask, noMask *int64) {

	///将牌必须是5
	if v.Jiang != 4 {
		return
	}
	//顺子数必须是4个
	if len(v.Array_shun) != 4 {
		return
	}
	//1 和9 开头的顺子必须都有两个
	num1 := 0
	num9 := 0
	for _, m := range v.Array_shun {
		if m == 0 {
			num1++
		}
		if m == 6 {
			num9++
		}
	}
	if num1 != 2 || num9 != 2 {
		return
	}
	*mask |= mahjong.YISESHUANGLONGHUI
	*noMask |= mahjong.PINGHU | mahjong.QIDUI | mahjong.QINGYISE | mahjong.YIBANGAO | mahjong.LAOSHAOFU

}

//一色四同顺
//牌里有一种花色且序数相同的4副顺子。不记番:一色三节高、一般高、四归一,一色三同顺、七对。
func IsYiSeSiTongShun(v *mahjong.MahjongCount, mask, noMask *int64) {

	//顺子数要4个
	if len(v.Array_shun) != 4 {
		return
	}
	//顺子要一样
	bo := true
	f := v.Array_shun[0]
	for _, m := range v.Array_shun {
		if f != m {
			bo = false
			break
		}
	}
	if !bo {
		return
	}
	*mask |= mahjong.YISESITONGSHUN
	*noMask |= mahjong.YISESANJIEGAO | mahjong.YIBANGAO | mahjong.SIGUIYI | mahjong.YISESANTONGSHUN | mahjong.QIDUI

}

//一色四节高
//胡牌时牌里有一种花色且序数依次递增一位数的4副刻子(或杠子)。不记番:一色三同顺、一色三节高、碰碰和。
func IsYiSeSiJieGao(v *mahjong.MahjongCount, mask, noMask *int64) {

	//杠或者刻子数只能是四个
	if len(v.Array_ke) != 4 && len(v.Array_gang) != 4 {
		return
	}
	a := make([]byte, 0)
	a = v.Array_ke[:]
	if len(v.Array_gang) == 4 {
		a = v.Array_gang[:]
	}
	//最后一个刻或者杠 与第一个杠或者刻相差3(因为Array_ke 和Array_gang是经过排序的)
	if a[3]-a[0] != 3 {
		return
	}
	*mask |= mahjong.YISESIJIEGAO
	*noMask |= mahjong.YISESANTONGSHUN | mahjong.YISESANJIEGAO | mahjong.PENGPENGHU

}

//一色四步高
//胡牌时,牌里有一种花色4副依次递增一位数或依次递增二位数的顺子。不记番:一色三步高。
//递增一位:123 234 345 456 北北  递增两位: 123 345 567 789 北北
func IsYiSeSiBuGao(v *mahjong.MahjongCount, mask, noMask *int64) {

	//顺子数必须是4个
	if len(v.Array_shun) != 4 {
		return
	}
	//	第一个顺子和第二个顺子之间的差
	c := v.Array_shun[1] - v.Array_shun[0]
	if c != 1 && c != 2 { //相差必须是1 或者2
		return
	}
	bo := true
	//从第三个顺子开始判断,
	for i := 2; i < len(v.Array_shun); i++ {
		if v.Array_shun[i]-v.Array_shun[i-1] != c {
			bo = false
			break
		}
	}
	if !bo {
		return
	}
	*mask |= mahjong.YISESIBUGAO
	*noMask |= mahjong.YISESANBUGAO

}

//三杠
//胡牌时,牌里有3副杠,明杠暗杠均可。
func IsSanGang(v *mahjong.MahjongCount, mask, noMask *int64) {

	//必须三幅杠
	if len(v.Array_gang) != 3 {
		return
	}
	*mask |= mahjong.SANGANG

}

//混幺九
//胡牌时,由字牌和序数牌、九的刻子及将牌组成的牌型。不记番:碰碰和、幺九刻、全带么。
func IsHunYaoJiu(v *mahjong.MahjongCount, mask, noMask *int64) {

	//刻子数至少两个
	if len(v.Array_ke) < 2 {
		return
	}
	ziKe := 0
	yjKe := 0
	for _, m := range v.Array_ke {
		if m == 0 || m == 8 {
			yjKe ++
		} else if m > 30 {
			ziKe ++
		}
	}
	if yjKe != 2 {
		return
	}
	if ziKe < 1 {
		return
	}
	*mask |= mahjong.HUNYAOJIU
	*noMask |= mahjong.PENGPENGHU | mahjong.YAOJIUKE | mahjong.QUANDAIYAO

}

//七对
//胡牌时,胡牌时,牌型由7个对子组成。(不计门前清、不求人、单钓将)
func IsQiDui(v *mahjong.MahjongCount, mask, noMask *int64) {

	if !v.QiDui {
		return
	}
	*mask |= mahjong.QIDUI
	*noMask |= mahjong.MENQIANQING | mahjong.BUQIUREN | mahjong.DANDIAOJIANG

}

//清一色
//胡牌时,牌型由一种花色的顺序牌组成
func IsQingYiSe(v *mahjong.MahjongCount, mask, noMask *int64) {

	//检查将牌 刻子 顺子 杠 是否是同一色(二人麻将只需判断是否是万牌)
	if v.Jiang > 8 {
		return
	}
	//最大一个刻必须是万
	if len(v.Array_ke) > 0 && v.Array_ke[len(v.Array_ke)-1] > 8 {
		return
	}
	//最大杠必须是万
	if len(v.Array_gang) > 0 && v.Array_gang[len(v.Array_gang)-1] > 8 {
		return
	}
	//顺子不需要判断,因为二人麻将只有万才会有顺子
	*mask |= mahjong.QINGYISE

}

//一色三同顺
//胡牌时,牌里有一种花色且依次递增一位数字的3副顺子。不记番: 一色三节高、一般高
func IsYiSeSanTongShun(v *mahjong.MahjongCount, mask, noMask *int64) {

	l := len(v.Array_shun)
	//顺子必须三幅以上
	if l < 3 {
		return
	}
	num := 1
	for i := 0; i < l-1; i++ {
		if v.Array_shun[i] == v.Array_shun[i+1] {
			num++
		} else if i == 1 {
			num = 0
		}
	}
	if num < 3 {
		return
	}
	*mask |= mahjong.YISESANTONGSHUN
	*noMask |= mahjong.YISESANJIEGAO | mahjong.YIBANGAO

}

//一色三节高
//胡牌时,牌里有一种花色且依次递增一位数字的3副刻子。不记番: 一色三同顺
func IsYiSeSanJieGao(v *mahjong.MahjongCount, mask, noMask *int64) {

	//刻子数至少是三个
	l := len(v.Array_ke)
	if l < 3 {
		return
	}
	num := 1
	for i := 0; i < l-1; i++ {
		if v.Array_ke[i]+1 == v.Array_ke[i+1] {
			num++
		} else if i == 1 {
			break
		}
	}
	if num < 3 {
		return
	}
	*mask |= mahjong.YISESANJIEGAO
	*noMask |= mahjong.YISESANTONGSHUN

}

//清龙
//胡牌时,有一种相同花色的123,456,789三付顺子即可。清龙就是清一色条龙。不记番:连6、老少副。
func IsQingLong(v *mahjong.MahjongCount, mask, noMask *int64) {

	//顺子至少三个
	l := len(v.Array_shun)
	if l < 3 {
		return
	}
	num1 := false
	num4 := false
	num7 := false
	//遍历顺子,判断是否存在 1 4 7 开头的顺子。
	for _, m := range v.Array_shun {
		if m == 0 {
			num1 = true
		} else if m == 3 {
			num4 = true
		} else if m == 6 {
			num7 = true
		}
	}
	if num1 && num4 && num7 {
		*mask |= mahjong.QINGLONG
		*noMask |= mahjong.LIANLIU | mahjong.LAOSHAOFU
	}

}

//一色三步高
//胡牌时,牌里有一种花色的牌,依次递增一位或依次递增二位数字的3副顺子。
/*
123 234 345 777 3131
123 345 456 567 303030 3131
 */
func IsYiSeSanBuGao(v *mahjong.MahjongCount, mask, noMask *int64) {

	//顺子三个以上
	l := len(v.Array_shun)
	if l < 3 {
		return
	}
	//相差1
	n1 := 0
	n2 := 0
	for i := 0; i < l-1; i++ {
		if v.Array_shun[i+1]-v.Array_shun[i] == 1 {
			n1 ++
		} else if v.Array_shun[i+1]-v.Array_shun[i] == 2 {
			n2 ++
		}
	}
	if n1 < 2 && n2 < 2 {
		return
	}
	c := byte(1)
	if n2 > 1 {
		c = 2
	}
	if l == 3 {
		*mask |= mahjong.YISESANBUGAO
	} else {
		if v.Array_shun[2]-v.Array_shun[1] == c {
			*mask |= mahjong.YISESANBUGAO
		}
	}

}

//天听
///庄家打出第-张牌时报听称为天听;发完牌后闲家便报听也称为天听。天听要在胡牌后才算番。如果庄家在发完牌后有暗杠,则庄家不算天听。如果发完牌之后有补花,补花之后报听也算天听。
func IsTianTing(v *mahjong.MahjongCount, mask, noMask *int64) {

}

//三暗刻
//胡牌时,牌里有3个暗刻。
func IsSanAnKe(v *mahjong.MahjongCount, mask, noMask *int64) {

	//暗刻必须三个
	if len(v.Array_a_ke) < 3 {
		return
	}
	*mask |= mahjong.SANANKE

}

//大于五
//胡牌时,牌型由序数牌6 9的顺子、刻子、将牌组成。(二人麻将只需判断万牌)
func IsDaYuWu(v *mahjong.MahjongCount, mask, noMask *int64) {

	sum := byte(0)
	//判断所有牌索引是否在5-8之间(至少是14张牌)
	for i := 5; i < 9; i++ {
		sum += v.TileIndexs[i]
	}
	if sum < 14 {
		return
	}
	*mask |= mahjong.DAYUWU

}

//小于五
//胡牌时,牌型由序数牌1-4的顺子、刻子、将牌组成。
func IsXiaoYuWu(v *mahjong.MahjongCount, mask, noMask *int64) {

	sum := byte(0)
	for i := 0; i < 5; i++ {
		sum += v.TileIndexs[i]
	}
	if sum < 14 {
		return
	}
	*mask |= mahjong.XIAOYUWU
}

//三风刻
//胡牌时,牌里有3个风刻
func IsSanFengKe(v *mahjong.MahjongCount, mask, noMask *int64) {
	//刻子必须有三个以上
	if len(v.Array_ke) < 3 {
		return
	}
	sum := 0
	for _, m := range v.Array_ke {
		if m < 31 && m > 26 {
			sum++
		}
	}
	if sum > 2 {
		*mask |= mahjong.SANFENGKE
	}
}

//妙手回春
//最后一张牌 ,自摸胡牌。不记番: 自摸。
func IsMiaoShouHuiChun(v *mahjong.MahjongCount, mask, noMask *int64) {
	//必须是自摸
	if !v.Zimo {
		return
	}
	//牌唯一id必须是最后一张
	if v.HuId == MaxTiles-1 {
		*mask |= mahjong.MIAOSHOUHUICHUN
		*noMask |= mahjong.ZIMO
	}
}

//海底捞月
//胡最后一张打出来的牌
func IsHaiDiLaoYue(v *mahjong.MahjongCount, mask, noMask *int64) {
	//不能是自摸
	if v.Zimo {
		return
	}
	//牌必须是最后一张
	if v.HuId == MaxTiles-1 {
		*mask |= mahjong.HAIDILAOYUE
	}
}

//杠上开花
//胡牌时,开杠抓进的牌成胡牌。不记番:自摸。
func IsGangShangKaiHua(v *mahjong.MahjongCount, mask, noMask *int64) {
	//杠上开花在上层计算
}

//抢杠胡
//胡牌时,和别人自抓开明杠的牌。不记番:胡绝张。
func IsQiangGangHu(v *mahjong.MahjongCount, mask, noMask *int64) {
	//抢杠胡在上层计算
}

//碰碰胡
//胡牌时,牌型由4副刻子(或杠)、将牌组成。
func IsPengPengHu(v *mahjong.MahjongCount, mask, noMask *int64) {
	if len(v.Array_ke) != 4 && len(v.Array_gang) != 4 {
		return
	}
	*mask |= mahjong.PENGPENGHU
}

//双暗杠
//胡牌时,有2个暗杠
func IsShuangAnGang(v *mahjong.MahjongCount, mask, noMask *int64) {
	if len(v.Array_a_gang) == 2 {
		*mask |= mahjong.SHUANGANGANG
	}
}

//混一色
//胡牌时,牌型由一种花色序数牌及字牌组成
func IsHunYiSe(v *mahjong.MahjongCount, mask, noMask *int64) {
	//
	bo := false
	bo1 := false
	for i := byte(0); i < byte(9); i++ {
		if v.TileIndexs[i] > 0 {
			bo = true
			break
		}
	}
	for i := byte(27); i < mahjong.MAX_INDEX; i++ {
		if v.TileIndexs[i] > 0 {
			bo1 = true
			break
		}
	}
	if bo && bo1 {
		*mask |= mahjong.HUNYISE
	}
}

//全求人
//胡牌时,全靠吃牌、碰牌、单钓别人打出的牌胡牌。不记番:单钓。
func IsQuanQiuRen(v *mahjong.MahjongCount, mask, noMask *int64) {
	if len(v.Array_c_shun) != len(v.Array_shun) || len(v.Array_a_gang) != 0 || len(v.Array_a_ke) != 0 {
		return
	}
	*mask |= mahjong.QUANQIUREN
	*noMask |= mahjong.DANDIAOJIANG
}

//双箭刻
//胡牌时,牌里有2副箭刻(或杠)。不记番:箭刻。
func IsShuangJianKe(v *mahjong.MahjongCount, mask, noMask *int64) {
	if len(v.Array_ke) != 2 && len(v.Array_gang) != 2 {
		return
	}
	var a []byte
	if len(v.Array_ke) == 2 {
		a = v.Array_ke
	} else {
		a = v.Array_gang
	}
	bo := true
	for _, m := range a {
		if m < 31 {
			bo = false
			break
		}
	}
	if bo {
		*mask |= mahjong.SHUANGJIANKE
		*noMask |= mahjong.JIANKE
	}
}

//全带幺
//胡牌时,每副牌、将牌都有么或九牌。(胡牌时各组牌除了字牌都必须有一或九的序数牌)。
func IsQuanDaiYao(v *mahjong.MahjongCount, mask, noMask *int64) {
	//将牌必须是1 或9
	if v.Jiang != 0 && v.Jiang != 8 {
		return
	}
	//顺子中必须有1 或9
	for _, m := range v.Array_shun {
		if m != 0 && m != 7 {
			return
		}
	}
	//刻子、杠必须是1 或者9
	var a []byte
	a = append(append(a, v.Array_ke...), v.Array_shun...)
	for _, m := range a {
		if m != 0 && m != 8 {
			return
		}
	}
	*mask |= mahjong.QUANDAIYAO
}

//不求人
//胡牌时,4副牌及将中没有吃牌、碰牌(包括明杠),自摸胡牌。
func IsBuQiuRen(v *mahjong.MahjongCount, mask, noMask *int64) {
	//必须是自摸
	if !v.Zimo {
		return
	}
	//不能有吃、碰、和明杠
	if len(v.Array_c_shun) != 0 || len(v.Array_a_ke) != len(v.Array_ke) || len(v.Array_m_gang) != 0 {
		return
	}
	*mask |= mahjong.BUQIUREN
}

//双明杠
//胡牌时,牌里有2个明杠。不记番:明杠。
func IsShuangMingGang(v *mahjong.MahjongCount, mask, noMask *int64) {
	//必须两个明杠
	if len(v.Array_m_gang) != 2 {
		return
	}
	*mask |= mahjong.SHUANGMINGGANG
	*noMask |= mahjong.MINGGANG
}

//胡绝张
//胡牌时,胡牌池、桌面已亮明的3张牌所剩的第4张牌。
func IsHuJueZhang(v *mahjong.MahjongCount, mask, noMask *int64) {
	//在上层实现
}

//箭刻
//胡牌时,牌里有中、发、白,这3个牌中的任一个牌组成的1副刻子
func IsJianKe(v *mahjong.MahjongCount, mask, noMask *int64) {

	for _, m := range v.Array_ke {
		if m > 29 {
			*mask |= mahjong.JIANKE
			return
		}
	}
}

//门风刻
//胡牌时牌里有与门风相同的风刻。
func IsMenFengKe(v *mahjong.MahjongCount, mask, noMask *int64) {
	//刻子
	if len(v.Array_ke) < 1 {
		return
	}
	for _, m := range v.Array_ke {
		if m == v.MenFeng {
			*mask |= mahjong.MENFENGKE
			return
		}
	}
}

//门前清
//没有吃、碰、明杠,和别人打出的牌。
func IsMenQianQing(v *mahjong.MahjongCount, mask, noMask *int64) {
	///不能是自摸
	if v.Zimo {
		return
	}
	if len(v.Array_c_shun) != 0 || len(v.Array_a_ke) != len(v.Array_ke) || len(v.Array_m_gang) != 0 {
		return
	}
	*mask |= mahjong.MENQIANQING
}

//平胡
//胡牌时,牌型由4副顺子及序数牌作将组成。边、坎、钓不影响平和。
func IsPingHu(v *mahjong.MahjongCount, mask, noMask *int64) {
	//将必须是1-9
	if v.Jiang > 8 {
		return
	}
	//顺子有四个
	if len(v.Array_shun) != 4 {
		return
	}
	*mask |= mahjong.PINGHU
}

//四归一
//胡牌时,牌里有4张相同的牌归于一家的顺、刻子、对、将牌中(不包括杠牌)。
func IsSiGuiYi(v *mahjong.MahjongCount, mask, noMask *int64) {
	//找到四张一样的牌(可以排除字牌)
	s := make([]byte, 0)
	for i, m := range v.TileIndexs {
		if m == 4 {
			s = append(s, byte(i))
		}
	}
	//遍历这些四张一样的牌,只要不是杠,就可以
	for _, c := range s {
		bo := true
		for _, k := range v.Array_gang {
			if k == c {
				bo = false
				break
			}
		}
		if bo {
			*mask |= mahjong.SIGUIYI
			return
		}
	}
}

//双暗刻
//胡牌时,牌里有2个暗刻。
func IsShuangAnKe(v *mahjong.MahjongCount, mask, noMask *int64) {
	if len(v.Array_a_ke) == 2 {
		*mask |= mahjong.SHUANGANKE
	}
}

//暗杠
//胡牌时,牌里有一副自抓4张相同的牌且开杠
func IsAnGang(v *mahjong.MahjongCount, mask, noMask *int64) {
	if len(v.Array_a_gang) > 0 {
		*mask |= mahjong.ANGANG
	}
}

//断幺
//胡牌时,牌里没有一、九牌及字牌。
func IsDuanYao(v *mahjong.MahjongCount, mask, noMask *int64) {
	//没有1 和9
	if v.TileIndexs[0] != 0 || v.TileIndexs[8] != 0 {
		return
	}
	//判断字牌
	for i := byte(27); i < mahjong.MAX_INDEX; i++ {
		if v.TileIndexs[i] != 0 {
			return
		}
	}
	*mask |= mahjong.DUANYAO
}

//报听
//报听后胡牌。
func IsBaoTing(v *mahjong.MahjongCount, mask, noMask *int64) {
	if v.TingBool {
		*mask |= mahjong.BAOTING
	}
}

//一般高
//胡牌时,牌里有一种花色且序数相同的2副顺子。
func IsYiBanGao(v *mahjong.MahjongCount, mask, noMask *int64) {
	//顺子数 必须是2个以上
	if len(v.Array_shun) < 2 {
		return
	}
	bo := false
	for i, m := range v.Array_shun {
		for j := i + 1; j < len(v.Array_shun); j++ {
			if v.Array_shun[j] == m {
				bo = true
				break
			}
		}
		if bo {
			*mask |= mahjong.YIBANGAO
			return
		}
	}
}

//连六
//一种花色六张序数相连的顺子(例如: 3-4-5条和6-7-8条)
func IsLianLiu(v *mahjong.MahjongCount, mask, noMask *int64) {
	if len(v.Array_shun) < 2 {
		return
	}
	for i := 0; i < len(v.Array_shun)-1; i++ {
		if v.Array_shun[i+1]-v.Array_shun[i] == 3 {
			*mask |= mahjong.LIANLIU
			return
		}
	}
}

//老少副
//胡牌时,牌里花色相同的123、789的顺子各一副。
func IsLaoShaoFu(v *mahjong.MahjongCount, mask, noMask *int64) {
	if len(v.Array_shun) < 2 {
		return
	}
	o := 0
	y := 0
	for _, m := range v.Array_shun {
		if o > 0 && y > 0 {
			*mask |= mahjong.LAOSHAOFU
			return
		}
		if m == 0 {
			y++
		} else if m == 8 {
			o++
		}
	}
}

//幺九刻
//胡牌时,牌里有序数为一、九的一副刻子(杠)或是字牌的一副刻子(杠)。
func IsYaoJiuKe(v *mahjong.MahjongCount, mask, noMask *int64) {
	var a []byte
	a = append(append(a, v.Array_ke...), v.Array_gang...)
	for _, m := range a {
		if m == 0 || m == 8 || (m > 26 && m < 31) {
			*mask |= mahjong.YAOJIUKE
			return
		}
	}
}

//明杠
//自己有暗刻,碰别人打出的一张相同的牌开杠:或自己抓进一张与碰的明刻相同
func IsMingGang(v *mahjong.MahjongCount, mask, noMask *int64) {
	if len(v.Array_m_gang) > 0 {
		*mask |= mahjong.MINGGANG
	}
}

//边张
//单和123的3及789的7或1233和3、7789和7都为边张。手中有12345和3,56789和7不算边张。
//123 3 胡得那张牌只能是顺子的两边牌,两个相邻的顺子不能有连接)
func IsBianZhang(v *mahjong.MahjongCount, mask, noMask *int64) {
	if v.HuIndex != 2 && v.HuIndex != 6 {
		return
	}
	for _, m := range v.Array_h_shun {
		if v.HuIndex == 2 {
			if m == 2 {
				return
			}
		} else if v.HuIndex == 6 && m+2 == 6 {
			return
		}
	}
	*mask |= mahjong.BIANZHANG
}

//坎张
//胡牌时,和2张牌之间的牌。4556和5也为坎张,手中有45567和6不算坎张。
func IsKanZhang(v *mahjong.MahjongCount, mask, noMask *int64) {
	if v.HuIndex == 0 || v.HuIndex == 8 {
		return
	}
	//遍历手牌顺子,胡的那张牌必须在中间,
	for _, m := range v.Array_h_shun {
		if v.HuIndex == m || v.HuIndex == m+2 {
			return
		}
	}
	*mask |= mahjong.KANZHANG
}

//单钓将
//钓单张牌作将成和
func IsDanDiaoJiang(v *mahjong.MahjongCount, mask, noMask *int64) {
	if v.HuIndex == v.Jiang {
		*mask |= mahjong.DANDIAOJIANG
	}
}

//自摸
//自己抓进牌成胡牌
func IsZiMo(v *mahjong.MahjongCount, mask, noMask *int64) {
	if v.Zimo {
		*mask |= mahjong.ZIMO
	}
}

//二五八将
//胡牌时,将牌是二万、五万、八万。
func IsErWuBaJiang(v *mahjong.MahjongCount, mask, noMask *int64) {
	if v.Jiang == 1 || v.Jiang == 4 || v.Jiang == 7 {
		*mask |= mahjong.ERWUBAJIANG
	}
}

猜你喜欢

转载自blog.csdn.net/ywloveb/article/details/86570054
今日推荐