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
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 |
|
}
|
|
|