golang实现最简单的麻将胡牌算法(不包括牌型,有需求后续可以更新牌型计算)

话不多说,代码先给出来 文件名 mahjong.go
很多的实现方法,写的注释我觉得已经很详细了,多看下注释。

package main

import (
	"sort"
	"fmt"
)

//牌类型
type CardType int

const (
	CardType_Unknown CardType = iota
	CardType_W
	CardType_T
	CardType_S
)

//实现string
func (st CardType) String() string {
	str := ""
	switch st {
	case CardType_W:
		str += "W"
	case CardType_T:
		str += "T"
	case CardType_S:
		str += "S"
	}
	return str
}

//牌定义
type Card struct {
	Type  CardType
	Value int
}

//实现string
func (c *Card) String() string {
	return fmt.Sprint("Card: ", c.Value, " ", c.Type)
}

//自定义排序
type SortCards []*Card

func (sc SortCards) Len() int {
	return len(sc)
}

func (sc SortCards) Less(i, j int) bool {
	if sc[i].Type != sc[j].Type {
		return sc[i].Type < sc[j].Type
	} else {
		return sc[i].Value < sc[j].Value
	}
	panic("unknown...")
}

func (sc SortCards) Swap(i, j int) {
	sc[i], sc[j] = sc[j], sc[i]
}

//实现
func (sc SortCards) Sort() {
	sort.Sort(sc)
}

//最简单的胡牌算法
/*
1.首先我们要明白,什么样的牌可以胡牌?
2.  4*3+2,7*2 可以胡牌;当然,这是手上有14张牌的时候,所以用公式来说就是 N*3 + 1*2 (0<=n<=4)
3. 再者,麻将有不同的花色,而且每一个花色是没有关联的,所以可以分开单独计算,随便还可以加个携程,还能提高效率(单核机当我没说),多好啊。
总结:说简单点就是 先分开花色,在每一个花色中计算能否符合条件,只要一个不符合,直接false。
*/
func AnalyzeCardsSimple(cards []*Card) bool {

	//有个七对,特殊判断一下就可以了
	if len(cards) ==14 {
		// todo 计算是否有七个对子
	}
	//用一个slice来装不同花色的牌
	diffColorCards := GetDiffColorCards(cards)
	fmt.Println(diffColorCards)
	for _,tempCards := range diffColorCards {
		//1-9 每张牌的数量
		cardsNum := make([]int,10)
		for _, card := range tempCards {
			cardsNum[card]++
		}
		isHu := SplitCards(cardsNum,false)
		fmt.Println(isHu)
		if !isHu {
			return false
		}
	}
	return true
}

/**
这是目前最简单的单机拆牌,只能判断是否能胡,不能用于计算牌型方面的东西。
原理:
	从第一张牌开始计算,
	假如一个牌有4张,在整个牌里面他只能做刻字和一个顺子;除开 333344445555 这种特殊情况,但是拆分出来也是判断可以胡的。

	所以减去三张牌,在调用SplitCards,这个时候它的第一张牌就只有一张,自然而然的就走找顺子的道路上了。
	但是减去三张发现后面也没有办法胡,看代码继续走下面,再减去2张试试呢。比如 22223344 这种牌

	一张牌它就只能去找后面的顺子,没有就不能胡。

	这里还有一个问题,就是有重复计算的部分
	比如 33334567 的牌,减去三个 3 剩下 34567,减去345剩67 不能胡;
	在回来减去两个 3 剩下 334567 ,在减去345剩下367不能胡;
	在回来到下面减一个345 剩33367,减去333 剩下67 ,这里和第一次其实是一样的算法,只是顺序不同。

 */
//对子只能存在一对。
func SplitCards(cardsNum []int,hasPair bool) bool {
	cnt := 0
	for _, num := range cardsNum {
		if num > 0 {
			break
		}
		cnt++
	}
	//判断没有牌为可以胡牌
	if len(cardsNum) == cnt {
		return true
	}
	for i := 0; i < len(cardsNum); i++ {
		switch cardsNum[i] {
		case 4:
			fallthrough
		case 3:
			//这种存在这几种情况,可以加后面成顺子,取两张为对子,或取一个刻字
			//减掉后再传入SplitCards
			cardsNum[i]-=3
			if SplitCards(cardsNum,hasPair) {
				return true
			}
			cardsNum[i]+=3
			//这种不行就向下传递。。。
			fallthrough
		case 2:
			if !hasPair {
				hasPair = true
				cardsNum[i]-=2
				if SplitCards(cardsNum,hasPair) {
					return true
				}
				cardsNum[i]+=2
			}
			fallthrough
		case 1:
			if i+2 < len(cardsNum) && cardsNum[i+1] > 0 &&  cardsNum[i+2] >0 {
				cardsNum[i]--
				cardsNum[i+1]--
				cardsNum[i+2]--
				if SplitCards(cardsNum,hasPair) {
					return true
				}
				cardsNum[i]++
				cardsNum[i+1]++
				cardsNum[i+2]++
			}
		}
	}
	return false
}


//获取不同颜色的牌
func GetDiffColorCards(cards []*Card) map[CardType][]int {
	colorCards := make(map[CardType][]int)
	for _, card := range cards {
		if _,ok := colorCards[card.Type]; !ok {
			colorCards[card.Type] = append([]int{},card.Value)
		}else {
			colorCards[card.Type] = append(colorCards[card.Type],card.Value)
		}
	}
	return colorCards
}

测试代码:mahjong_test.go

package main

import (
	"testing"
	"fmt"
)

func TestSortCards(t *testing.T) {
	cards := []*Card{
		&Card{CardType_W,1},
		&Card{CardType_W,1},
		&Card{CardType_W,1},
		&Card{CardType_W,2},
		&Card{CardType_W,2},
		&Card{CardType_W,2},
		&Card{CardType_T,3},
		&Card{CardType_T,3},
		&Card{CardType_T,4},
		&Card{CardType_T,4},
		&Card{CardType_S,3},
		&Card{CardType_S,3},
		&Card{CardType_S,5},
		&Card{CardType_S,5},
	}
	SortCards(cards).Sort()
	fmt.Println(cards)
	//tempCards := make([]*Card, len(cards))
	//copy(tempCards,cards)
	//fmt.Println(tempCards)
	//temp := append([]*Card{}, &Card{CardType_S, 7})
	//fmt.Println(temp)
	//diffColorCards := GetDiffColorCards(cards)
	//fmt.Println(diffColorCards)
	//for _,tempCards := range diffColorCards {
	//	fmt.Println(tempCards)
	//}
	simple := AnalyzeCardsSimple(cards)
	fmt.Println(simple)
}

当然,这代码是第一个版本,其中还有更多可以优化。
那得看有需要优化需求的朋友是不是多了。
请留言我会实时收到消息。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值