golang制作一个斗地主游戏服务器[5]:牌型

下面就该进入打牌环节了

打牌之前, 首先应该定义牌的类型有哪些.

斗地主里面分成 

单牌,  对子, 三不带, 三带一

拖拉机, 顺子, 飞机

王炸

其中,   拖拉机最多10对, 顺子至少5张 最多12张

飞机内容比较多

我们先把所有可能出现的牌型列举出来



// 牌型
const (
	cardTypeNIL int = iota // 留空
	// 单牌
	cardTypeSINGLE
	// 对子
	cardTypeDUAD
	// 三干
	cardTypeTRIPLE0
	// 三带一
	cardTypeTRIPLE1
	// 3对拖拉机
	cardTypeTRACTOR3
	// 4对拖拉机
	cardTypeTRACTOR4
	// 5对拖拉机
	cardTypeTRACTOR5
	// 6对拖拉机
	cardTypeTRACTOR6
	// 7对拖拉机
	cardTypeTRACTOR7
	// 8对拖拉机
	cardTypeTRACTOR8
	// 9对拖拉机
	cardTypeTRACTOR9
	// 10对拖拉机
	cardTypeTRACTOR10
	// 5张顺子
	cardTypeSTRAIGHT5
	// 6张顺子
	cardTypeSTRAIGHT6
	// 7张顺子
	cardTypeSTRAIGHT7
	// 8张顺子
	cardTypeSTRAIGHT8
	// 9张顺子
	cardTypeSTRAIGHT9
	// 10张顺子
	cardTypeSTRAIGHT10
	// 11张顺子
	cardTypeSTRAIGHT11
	// 12张顺子
	cardTypeSTRAIGHT12
	// 飞机不带
	cardTypePLANE20
	// 3飞机不带
	cardTypePLANE30
	// 4飞机不带(这里其实可以当做3飞机带3单) cardTypePLANE31
	cardTypePLANE40
	// 5飞机不带
	cardTypePLANE50
	// 6飞机不带
	cardTypePLANE60

	// 飞机带2单
	cardTypePLANE21
	// 3飞机带3单
	cardTypePLANE31
	// 4飞机带4单
	cardTypePLANE41
	// 5飞机带5单
	cardTypePLANE51

	// 炸弹
	cardTypeBOMB
	// 王炸火箭
	cardTypeROCKET
)

计算牌型前别忘记先洗一次牌

首先分析1~4张牌的牌型

1张肯定是单牌

// 单张
func (self TCards) getCardType1() (int, int) {
	return cardTypeSINGLE, self[0].nPoint // 单张的只有单牌
}

2张牌可能是王炸或者对子

// 两张牌
func (self TCards) getCardType2() (int, int) {
	if self[0].nPoint == cardPointX && self[1].nPoint == cardPointY {
		return cardTypeROCKET, self[0].nPoint // 王炸
	}

	if self[0].nPoint == self[1].nPoint {
		return cardTypeDUAD, self[0].nPoint // 一样的是对子
	}

	return cardTypeNIL, 0
}

3张牌只能是3不带

// 三张牌, 只可能是三干
func (self TCards) getCardType3() (int, int) {
	if self[0].nPoint == self[1].nPoint && self[0].nPoint == self[2].nPoint {
		return cardTypeTRIPLE0, self[0].nPoint // 三张一样单牌
	}
	return cardTypeNIL, 0
}

4张牌只能是三带一或者炸弹

// 四张牌, 可能是三带一 或者
func (self TCards) getCardType4() (int, int) {
	if self[1].nPoint != self[2].nPoint {
		return cardTypeNIL, 0 // 如果中间两张不相等就没意义了
	}

	if self[0].nPoint == self[3].nPoint {
		return cardTypeBOMB, self[1].nPoint // 如果首位两张也相等(因为是排序的, 通过夹逼准则, 四张牌都相等)
	}

	if self[0].nPoint == self[1].nPoint || self[1].nPoint == self[3].nPoint {
		return cardTypeTRIPLE1, self[1].nPoint // 只要有一个相等就是三带一
	}

	return cardTypeNIL, 0
}

超过5张牌的情况就比较复杂了.....  顺子,  拖拉机,  飞机等其他情况

我们依次来做判断

首先是比较简单的顺子算法


// 可以检查是否是顺子
func (self TCards) checkCardTypeStraight() (int, int) {
	nFirst := self[0].nPoint // 起始的牌
	nLen := len(self)

	// 顺子最少5张, 最多3 -> A = 12 张
	if nLen < 5 || nLen > 12 {
		return cardTypeNIL, cardPointNIL
	}

	// 按顺序计算是否正常
	for i := 1; i < nLen; i++ {
		if self[i].nPoint-nFirst != i {
			return cardTypeNIL, cardPointNIL
		}
	}

	// 到了A就要截止
	if self[nLen-1].nPoint > cardPointA {
		return cardTypeNIL, cardPointNIL
	}

	return cardTypeSTRAIGHT5 + nLen - 5, self[0].nPoint
}

再次是同样简单的拖拉机


// 6张牌可以检查是否是拖拉机
func (self TCards) checkCardTypeTractor() (int, int) {
	nFirst := self[0].nPoint
	nLen := len(self)

	// 必须是双数卡组才可能是拖拉机
	if nLen%2 == 1 {
		return cardTypeNIL, cardPointNIL
	}

	// 最少3对,  最多10对
	if nLen < 6 || nLen > 20 {
		return cardTypeNIL, cardPointNIL
	}

	// 第一张拖拉机对子
	if self[1].nPoint != nFirst {
		return cardTypeNIL, cardPointNIL
	}

	// 循环后面的拖拉机对子
	for i := 1; i < nLen/2; i++ {
		if self[2*i].nPoint-nFirst != i {
			return cardTypeNIL, cardPointNIL
		}

		if self[2*i+1].nPoint-nFirst != i {
			return cardTypeNIL, cardPointNIL
		}
	}

	return cardTypeTRACTOR3 + nLen/2 - 3, self[0].nPoint // 6张 3对拖拉机起, 长度+2 就是+1
}

飞机分成3个不带的飞机和3个带1的两种. 有些部分还有3个带一对, 或者不到最后一手牌必须进行三带一 (本公司算法)

其中三个的部分和拖拉机差不多. 由于夹逼原则,  排序过后只需要第一第三张牌符合条件即可


// 飞机全是三不带
func (self TCards) getCardTypePlane3Only() (int, int) {
	nLen := len(self)

	// 基础数量判断
	if nLen < 6 || nLen > 18 {
		return cardTypeNIL, cardPointNIL
	}

	nFirst := self[0].nPoint
	// 3张牌做3张牌循环,  先三三检查是否满足
	if self[2].nPoint != nFirst {
		return cardTypeNIL, cardPointNIL
	}

	// 再检查是否依次递增
	for i := 1; i < nLen/3; i++ {
		if self[i*3].nPoint-nFirst != i {
			return cardTypeNIL, cardPointNIL
		}

		if self[i*3+2].nPoint-nFirst != i {
			return cardTypeNIL, cardPointNIL
		}
	}

	return cardTypePLANE20 + (nLen / 3) - 2, self[0].nPoint
}

三带一稍微复杂一点. 需要找到所有牌的数量, 然后进行检测


// 飞机是三带一
func (self TCards) getCardTypePlane3And1() (int, int) {
	nLen := len(self)
	var data [15]int
	// 数量
	for _, pCard := range self {
		data[pCard.nPoint]++
	}

	// 找到飞机主干
	findFun := func(nStart int) int {
		for i := nStart; i < 15; i++ {
			if data[i] > 3 {
				return i
			}
		}
		return 0
	}
	n1 := findFun(1)
	for i := 0; i < nLen/4; i++ {
		if n1 == 0 {
			return cardTypeNIL, cardPointNIL
		}

		n2 := findFun(n1)
		if n2 != n1+1 {
			return cardTypeNIL, cardPointNIL
		}

		n1 = n2
	}

	return cardTypePLANE21 + (nLen / 4) - 2, n1
}

到此为止,  牌型的部分暂时告一段落.

猜你喜欢

转载自blog.csdn.net/warrially/article/details/88635058