You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

349 lines
8.2 KiB

package game
const (
HandNoType HandType = (iota << 8) | 0 // 无牌型
HandPingHu HandType = (iota << 8) | 1 // 平胡
HandDaDuiZi HandType = (iota << 8) | 4 // 大对子
HandQingYiSe HandType = (iota << 8) | 4 // 清一色
HandQiDui HandType = (iota << 8) | 4 // 七对
HandLongQiDui HandType = (iota << 8) | 8 // 龙七对
HandQingDui HandType = (iota << 8) | 8 // 清一色大对子
HandQingQiDui HandType = (iota << 8) | 16 // 清七对
HandQingLongQiDui HandType = (iota << 8) | 32 // 清龙七对
HandShuangLongQiDui HandType = (iota << 8) | 32 // 双龙七对
HandShiBaLuoHan HandType = (iota << 8) | 4 // 十八罗汉 *4杠=64
HandQingShiBaLuoHan HandType = (iota << 8) | 16 // 清18罗汉 *4杠=256
)
type HandType int32
var handTypeNames = []string{"non", "平胡", "大对子", "清一色", "七对", "龙七对", "清对", "清七对", "清龙七对", "双龙七对", "十八罗汉", "清18罗汉"}
func (h HandType) String() string {
return handTypeNames[h>>8]
}
func (h HandType) Type() int8 {
return int8(h >> 8)
}
func (h HandType) Weight() int32 {
return int32(h) & 0xff
}
// 将双龙七对 64
// 十八罗汉 64
// 将三龙七对 126
// 清18罗汉 256
const (
WeightGang MjWeight = (iota << 8) | 2 // 杠
WeightZiMo MjWeight = (iota << 8) | 2 // 自摸
WeightGangShangKaiHua MjWeight = (iota << 8) | 2 // 杠上开花
WeightGangShangPao MjWeight = (iota << 8) | 2 // 杠上炮
WeightHaiDiLaoYue MjWeight = (iota << 8) | 2 // 海底捞月
WeightDuanYaoJiu MjWeight = (iota << 8) | 2 // 断幺九
WeightYaoJiu MjWeight = (iota << 8) | 4 // 幺九 (清幺九)
WeightJinGouDiao MjWeight = (iota << 8) | 4 // 金钩吊 (清勾勾)
WeightJiangDui MjWeight = (iota << 8) | 8 // 将对
WeightDiHu MjWeight = (iota << 8) | 32 // 地胡
WeightTianHu MjWeight = (iota << 8) | 32 // 天胡
)
type MjWeight int32
var weightNames = []string{"杠", "自摸", "杠上开花", "杠上炮", "海底捞月", "断幺九", "幺九", "金钩吊", "将对", "地胡", "天胡"}
func (w MjWeight) String() string {
return weightNames[w>>8]
}
func (w MjWeight) Type() int8 {
return int8(w >> 8)
}
func (w MjWeight) Weight() int32 {
return int32(w) & 0xff
}
type WinSettle struct {
Weight int32
WinType HandType
Weights []MjWeight
}
type HandWin struct {
Pairs [][2]int8
Triplets [][3]int8
handType HandType // 牌型
}
// GetWinType 计算胡牌牌型
func (win *HandWin) GetWinType(peng [][3]int8, gang [][4]int8) HandType {
win.handType = HandPingHu
if len(win.Pairs) == 7 { // 七对
win.handType = HandQiDui
qing := true
long := false // 龙七对, 手牌有一杠
for i, pair := range win.Pairs {
qing = qing && win.Pairs[0][0]/10 == pair[0]/10
long = long || (i > 0 && pair[0] == win.Pairs[i-1][0])
}
if qing { // 清七对
win.handType = HandQingQiDui
}
// TODO 龙七对, 将七对 ...
return win.handType
}
// 清一色, 大对子
pair := win.Pairs[0]
qing, dui := true, len(win.Triplets) > 0
for _, tile3 := range win.Triplets {
qing = qing && pair[0]/10 == tile3[0]/10
dui = dui && isTriplet(tile3)
}
if qing {
for _, peng := range peng {
qing = qing && pair[0]/10 == peng[0]/10
}
for _, gang := range gang {
qing = qing && pair[0]/10 == gang[0]/10
}
}
if qing && dui {
win.handType = HandQingDui // 清对
} else {
switch true {
case qing:
win.handType = HandQingYiSe
case dui:
win.handType = HandDaDuiZi
default:
win.handType = HandPingHu
}
}
// 十八罗汉
if len(gang) == 4 {
if win.handType == HandQingYiSe {
win.handType = HandQingShiBaLuoHan // 清十八罗汉
} else {
win.handType = HandShiBaLuoHan
}
}
return win.handType
}
// IsHuaZhu 是否花猪
func IsHuaZhu(lack int8, draw int8, hand []int8) bool {
if draw > 0 && draw/10 == lack {
return true
}
for _, card := range hand {
if card/10 == lack {
return true
}
}
return false
}
func getPairs(cards []int8) (pairs [][2]int8) {
for i := 0; i < len(cards)-1; i++ {
if cards[i] == cards[i+1] {
pairs = append(pairs, [2]int8{cards[i], cards[i+1]})
i++
}
}
return
}
func isQingYiSe(cards []int8, assemble []int8) bool {
cardType := cards[0] / 10
for _, card := range cards {
if card/10 != cardType {
return false
}
}
for _, card := range assemble {
if card/10 != cardType {
return false
}
}
return true
}
// GetHandWins 计算手牌可胡牌牌型
func GetHandWins(draw int8, hand []int8) (wins []HandWin) {
cards := InsertTile(draw, hand)
pairs := getPairs(cards)
// 无对胡不了
if len(pairs) == 0 {
return
}
// 单调
if len(cards) == 2 {
win := HandWin{Pairs: [][2]int8{pairs[0]}}
wins = append(wins, win)
return
}
if len(pairs) == 7 {
wins = append(wins, HandWin{Pairs: pairs})
return
}
if (len(cards)-2)%3 != 0 { // 除对子外剩余牌数量不能都组成刻
return
}
// 除眼外其他的牌都能组成刻
pairLoop:
for _, pair := range pairs {
var _ [][3]int8
rest, _ := FilterTiles(cards, pair[0], pair[1])
win := HandWin{Pairs: [][2]int8{pair}}
for len(rest) > 0 { // todo 统计所有牌型
tile3, ok := findAndRemoveTriplet(&rest)
if !ok {
tile3, ok = findAndRemoveSequence(&rest)
}
if !ok {
continue pairLoop
}
win.Triplets = append(win.Triplets, tile3)
}
wins = append(wins, win)
}
return
}
// InsertTile 插入单张手牌
func InsertTile(tile int8, sortedTiles []int8) (cards []int8) {
cards = make([]int8, len(sortedTiles)+1)
copy(cards, sortedTiles)
cards[len(cards)-1] = tile // 放到最后,最大则不变
for i := 0; i < len(sortedTiles); i++ {
if tile < sortedTiles[i] {
copy(cards[i+1:], cards[i:len(cards)-1])
cards[i] = tile
break
}
}
return
}
func InsertHand(lack int8, tile int8, soredHand []int8) (cards []int8) {
length := len(soredHand) + 1
cards = make([]int8, length)
copy(cards[1:], soredHand)
cards[0] = tile // 放到最前,最小则不变
score := tile
if tile/10 == lack {
score += 63
}
for i := length - 1; i >= 0; i-- {
handScore := cards[i]
if cards[i]/10 == lack {
handScore += 63
}
if score > handScore {
copy(cards[0:i], cards[1:i+1])
cards[i] = tile
break
}
}
return
}
func FilterTiles(cards []int8, tiles ...int8) (rest []int8, ok bool) {
tilesLen := len(tiles)
if tilesLen == 0 {
return cards, true
}
if len(cards) < tilesLen {
return rest, false
}
filters := make([]int8, tilesLen)
copy(filters, tiles)
rest = make([]int8, 0, len(cards)-tilesLen)
for _, card := range cards {
for j, tile := range filters {
if card == tile {
filters[j] = 0
tilesLen--
card = 0
break
}
}
if card > 0 {
rest = append(rest, card)
}
}
ok = tilesLen == 0
return
}
// findAndRemoveTriplet 从已排序的[]int中移除排头的刻子
func findAndRemoveTriplet(sortedCards *[]int8) (triplet [3]int8, ok bool) {
var v = *sortedCards
triplet = [3]int8{v[0], v[1], v[2]}
if ok = isTriplet(triplet); ok {
*sortedCards = append([]int8{}, v[3:]...)
}
return
}
// findAndRemoveSequence 从已排序的[]int中移除排头的顺子
func findAndRemoveSequence(sortedCards *[]int8) (sequence [3]int8, ok bool) {
var v = *sortedCards
var tmp = make([]int8, 0)
for i := 1; i < len(v); i++ {
switch {
case v[i] == v[i-1]:
tmp = append(tmp, v[i])
case v[i] == v[i-1]+1:
if ok = v[i]-v[0] == 2; ok {
tmp = append(tmp, v[i+1:]...)
*sortedCards = tmp
sequence = [3]int8{v[0], v[i-1], v[i]}
return
}
default:
return
}
}
return
}
// FindQuaternion 找到所有四元组
func FindQuaternion(cards []int8) (gangs [][4]int8) {
length := len(cards)
for i := 0; i < length-3; {
for j := 1; j < 4; j++ {
if cards[i] != cards[i+j] {
i += j
break
}
if j == 3 {
gangs = append(gangs, [4]int8{cards[i], cards[i], cards[i], cards[i]})
i += j
}
}
}
return
}
// isTriplet 是否刻子
func isTriplet(tile3 [3]int8) bool {
return tile3[0] == tile3[1] && tile3[1] == tile3[2]
}
// IsSequence 是否顺子
func isSequence(tile3 [3]int8) bool {
if tile3[0] > 29 || tile3[1] > 29 || tile3[2] > 29 {
return false
}
return tile3[1] == tile3[0]+1 && tile3[2] == tile3[1]+1
}