/*
1.查表法计算麻将胡牌(原理:http://hp.vector.co.jp/authors/VA046927/mjscore/mjalgorism.html)
2.跟拆解法对比进行效率比较
*/
package main
import (
"fmt"
"time"
"sort"
"os"
"encoding/json"
"log"
"runtime/debug"
"io/ioutil"
"io"
"math/rand"
)
type jsonData struct {
K int
V int
}
var g_cards = []int {
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, /* 筒 */
0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, /* 条 */
0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, /* 万 */
0x31, 0x41, 0x51, 0x61, 0x71, 0x81, 0x91, /* 东南西北中发白 */
}
func value2index(value int) int {
if value < 0x31 {
return ((value&0xF0)>>4)*9 + (value&0x0F) - 1
} else {
return 27 + ((value&0xF0)>>4) - 3
}
}
func count(cards []int) []int {
nums := make([]int, 34)
for _, v := range cards {
nums[value2index(v)]++
}
return nums
}
func calcKey(nums []int) int {
p := -1
x := 0
b := false
for i := 0; i < 3; i++ {
for j := 0; j < 9; j++ {
if (nums[i * 9 + j] == 0) {
if (b) {
b = false
x |= 0x1 << uint32(p)
p++
}
} else {
p++
b = true
switch (nums[i * 9 + j]) {
case 2:
x |= 0x3 << uint32(p)
p += 2
case 3:
x |= 0xF << uint32(p)
p += 4
case 4:
x |= 0x3F << uint32(p)
p += 6
}
}
}
if (b) {
b = false
x |= 0x1 << uint32(p)
p++
}
}
dong := value2index(0x31)
bai := value2index(0x91)
// 字牌
for i := dong; i <= bai; i++ {
if (nums[i] > 0) {
p++
switch (nums[i]) {
case 2:
x |= 0x3 << uint32(p)
p += 2
case 3:
x |= 0xF << uint32(p)
p += 4
case 4:
x |= 0x3F << uint32(p)
p += 6
}
x |= 0x1 << uint32(p)
p++
}
}
return x
}
/* 可以是这种牌型11 111,虽然1不可能有5张,但是可以胡1的.且最多有一种牌是5张牌的组合 */
func checkIsValid(nums []int) bool {
if (len(nums)%3 != 2) {
return false
}
n := 0
for _, v := range nums {
if v > 5 {
return false
}
if v == 5 {
n++
}
}
if n > 1 {
return false
}
return true
}
func getCardsNum(nums []int) int {
cardsNum := 0
for _, v := range nums {
if v > 0 {
cardsNum += v
}
}
return cardsNum
}
var printNum = 1
func encode(encodeData map[int]int, cards []int) {
//sort.Sort(sort.IntSlice(cards))
nums := count(cards)
if checkIsValid(nums) {
encodeData[calcKey(nums)] = 1
if len(encodeData) / 100 == printNum && len(encodeData) % 100 == 0 {
printNum++
fmt.Println("len(map)=", len(encodeData))
}
}
}
func getPairs() [][]int {
pairs := make([][]int, 0, len(g_cards))
for _, v := range g_cards {
pair := []int{v, v}
pairs = append(pairs, pair)
}
return pairs
}
func getGroups() [][]int {
groups := make([][]int, 0, len(g_cards)+(9-2)*3)
// find three identical tiles
for _, v := range g_cards {
group := []int{v, v, v}
groups = append(groups, group)
}
// find three sequence tiles
for i := 2; i < len(g_cards); i++ {
if g_cards[i-2]+1 == g_cards[i-1] && g_cards[i-1] == g_cards[i]-1 {
group := []int{g_cards[i-2], g_cards[i-1], g_cards[i]}
groups = append(groups, group)
}
}
return groups
}
/* 将所有胡牌牌型编码成json数据 */
func encodeCards(pairs [][]int, groups [][]int) map[int]int {
encodeData := make(map[int]int, 800)
for _, p := range pairs {
encode(encodeData, p)
for _, a := range groups {
var a_temp []int
a_temp = append(a_temp, p...)
a_temp = append(a_temp, a...)
encode(encodeData, a_temp)
for _, b := range groups {
var b_temp []int
b_temp = append(b_temp, a_temp...)
b_temp = append(b_temp, b...)
encode(encodeData, b_temp)
for _, c := range groups {
var c_temp []int
c_temp = append(c_temp, b_temp...)
c_temp = append(c_temp, c...)
encode(encodeData, c_temp)
for _, d := range groups {
var d_temp []int
d_temp = append(d_temp, c_temp...)
d_temp = append(d_temp, d...)
encode(encodeData, d_temp)
}
}
}
}
}
fmt.Println("-----------------七对----------------")
//七对
l := len(pairs)
temp := make([]int, 14)
for i := 0; i < l; i++ {
temp = append(temp[:0], pairs[i]...)
for j := i; j < l; j++ {
temp = append(temp[:2], pairs[j]...)
for m := i+1; m < l; m++ {
temp = append(temp[:4], pairs[m]...)
for n := m; n < l; n++ {
temp = append(temp[:6], pairs[n]...)
for x := m+1; x < l; x++ {
temp = append(temp[:8], pairs[x]...)
for y := x; y < l; y++ {
temp = append(temp[:10], pairs[y]...)
for u := x+1; u < l; u++ {
temp = append(temp[:12], pairs[u]...)
encode(encodeData, temp)
}
}
}
}
}
}
}
return encodeData
}
func huCards2JSON() {
begin := time.Now().UTC().UnixNano()
pairs := getPairs()
groups := getGroups()
m := encodeCards(pairs, groups)
f, err := os.Create("huCards.json")
defer f.Close()
if err != nil {
log.Fatal("Create", err)
}
var jd jsonData
enc := json.NewEncoder(f)
fmt.Println("map len = ", len(m))
for k, v := range m {
jd.K = k
jd.V = v
if err := enc.Encode(jd); err != nil {
log.Fatal("Encode", err)
}
}
fmt.Printf(", 花费时间:%vs", float32(float32(time.Now().UTC().UnixNano() - begin)/1000000000))
}
/* 配置文件只有一个json数据,如:{"K":30319,"V":1}, 有多个是会出错 */
func readOneJson() {
configPath := "huCards.json"
data, err := ioutil.ReadFile(configPath)
if err != nil {
log.Printf("%v", err)
}
var jd jsonData
err = json.Unmarshal(data, &jd)
if err != nil {
log.Printf("%v", err)
}
fmt.Println(jd)
}
func readAllJson() map[int]int {
f, err := os.Open("huCards.json")
defer f.Close()
if err != nil {
log.Printf("Open", err)
}
var jd jsonData
dec := json.NewDecoder(f)
m := make(map[int]int)
for {
if err := dec.Decode(&jd); err == io.EOF {
break
} else if err != nil {
log.Printf("Decode", err)
}
m[int(jd.K)] = jd.V
}
return m
}
var totalCardNums = []int{2, 5, 8, 11, 14}
func randCards(allCards []int, n int) []int {
if n <= 0 {
n = totalCardNums[rand.Int31n(int32(len(totalCardNums)))]
}
allLen := len(allCards)
var cards = make([]int, n)
for i := 0; i < n; i++ {
index := rand.Int31n(int32(allLen))
cards[i] = allCards[index]
allCards[allLen-1], allCards[index] = allCards[index], allCards[allLen-1]
allLen--
}
return cards
}
/* 判断胡牌-拆解法(3N+2) */
func huSplitMethod(nums []int, cardsNum int) bool {
length := len(nums)
if cardsNum == 0 {
return true
}
f := func(n int) bool {
for i := 0; i < length; i++ {
if nums[i] >= n {
nums[i] -= n
cardsNum -= n
if huSplitMethod(nums, cardsNum) {
return true
} else {
nums[i] += n
cardsNum += n
}
}
}
return false
}
//对子
if cardsNum % 3 == 2 {
return f(2)
}
//三张
if f(3) {
return true
}
//顺子
loop := [][]int{{0, 7}, {9, 16}, {18, 25}}
for j := 0; j < len(loop); j++ {
for i := loop[j][0]; i < loop[j][1]; i++ {
if nums[i] > 0 && nums[i+1] > 0 && nums[i+2] > 0 {
nums[i] -= 1
nums[i+1] -= 1
nums[i+2] -= 1
cardsNum -= 3
if huSplitMethod(nums, cardsNum) {
return true
} else {
nums[i] += 1
nums[i+1] += 1
nums[i+2] += 1
cardsNum += 3
}
}
}
}
return false
}
/* 胡牌-七对判断 */
func huQiDui(nums []int) bool {
for _, v := range nums {
if v != 0 && v != 2 && v != 4 {
return false
}
}
return true
}
func IsHu(nums []int) bool {
if !huQiDui(nums) {
cardsNum := getCardsNum(nums)
if cardsNum <= 0 || cardsNum > 14 || !checkIsValid(nums) || !huSplitMethod(nums, cardsNum) {
return false
}
}
return true
}
func test1_split(times int, allCards []int) {
for i := 0; i < times; i++ {
cards := randCards(allCards, 0)
nums := count(cards)
if IsHu(nums) {
}
}
}
func test1(times int, m map[int]int, allCards []int) {
for i := 0; i < times; i++ {
cards := randCards(allCards, 0)
nums := count(cards)
key := calcKey(nums)
_, ok := m[key]
if ok {
}
}
}
func test2(times int, m map[int]int, allCards []int) {
cards := randCards(allCards, 14)
//sort.Sort(sort.IntSlice(cards))
nums := count(cards)
for i := 0; i < times; i++ {
key := calcKey(nums)
_, ok := m[key]
if ok {
}
}
}
func test3(times int, m map[int]int, allCards []int) {
for i := 0; i < 9 ; i++ { //all yes
cards := []int{0x01, 0x01, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x09, 0x09}
cards = append(cards, []int{i+1}...)
sort.Sort(sort.IntSlice(cards))
nums := count(cards)
key := calcKey(nums)
_, ok := m[key]
if ok {
fmt.Println("A can hu:", i+1)
}
if IsHu(nums) {
fmt.Println("A can hu:", i+1)
}
}
cardsTmp := [][]int{
{0x21, 0x21}, //yes
{0x21, 0x28}, //no
{0x21, 0x21, 0x21, 0x21, 0x21}, //yes
{0x21, 0x21, 0x21, 0x11, 0x12}, //no
{0x91, 0x91, 0x22, 0x22, 0x22, 0x24, 0x25, 0x26, 0x26, 0x26, 0x27, 0x27, 0x28, 0x28}, //yes
{0x11, 0x11, 0x11, 0x11, 0x22, 0x23, 0x24}, //no
{0x21, 0x21, 0x21, 0x21, 0x22, 0X23, 0X37, 0X37}, //yes
{0x21, 0x22, 0x23, 0x24, 0x25, 0X26, 0X33, 0X33, 0X33, 0X33}, //no
{0x01, 0x01, 0x01, 0x02, 0x03, 0x01, 0x02, 0x03, 0x01, 0x02, 0x03, 0x02, 0x03, 0x04}, //yes
{0x02, 0x02, 0x01, 0x02, 0x03, 0x01, 0x02, 0x03, 0x01, 0x02, 0x03, 0x02, 0x03, 0x04}, //no
{0x11, 0x11, 0x21, 0x21, 0x22, 0X22, 0X31, 0X31, 0X61, 0X61, 0X61, 0X61, 0X07, 0X07}, //yes
{0x11, 0x11, 0x11, 0x21, 0x21, 0X31, 0X31, 0X31, 0X31, 0X61, 0X61, 0X61, 0X61, 0X07, 0X07}, //no
{0x11, 0x11, 0x21, 0x21, 0X31, 0X31, 0X31, 0X31, 0X61, 0X61, 0X61, 0X61, 0X07, 0X07}, //yes
{0x11, 0x11, 0x12, 0x12, 0X31, 0X31, 0X31, 0X31, 0X61, 0X61, 0X61, 0X61, 0x61, 0X07, 0X07}, //no
{0x11, 0x11, 0x12, 0x12, 0X31, 0X31, 0X31, 0X31, 0X61, 0X61, 0X61, 0X61, 0X07, 0X07}, //yes
{0x11, 0x11, 0x11, 0x11, 0X31, 0X31, 0X31, 0X31, 0X61, 0X61, 0X61, 0X61, 0X07, 0X07}, //yes
{0x11, 0x11, 0x11, 0x11, 0x12, 0x12, 0x12, 0x12, 0X13, 0X13, 0X13, 0X13, 0X14, 0X14}, //yes
}
for i := 0; i < len(cardsTmp) ; i++ {
cards := cardsTmp[i]
sort.Sort(sort.IntSlice(cards))
nums := count(cards)
key := calcKey(nums)
_, ok := m[key]
if ok {
fmt.Println("BB can hu:", i+1, "yes")
} else {
fmt.Println("BB cannot hu:", i+1, "no")
}
if IsHu(nums) {
fmt.Println("BB can hu:", i+1, "yes")
} else {
fmt.Println("BB cannot hu:", i+1, "no")
}
}
}
func benchmark() {
m := readAllJson()
fmt.Printf("len(map)=%v\n", len(m))
var allCards []int
for i := 0; i < 4; i++ {
allCards = append(allCards, g_cards...)
}
begin := time.Now().UTC().UnixNano()
times := 10000000
//test1(times, m, allCards)
test1_split(times, allCards)
//test2(times, m, allCards)
//test3(times, m, allCards)
fmt.Printf("计算胡牌%v次, 花费时间:%vs\n", times, float32(float32(time.Now().UTC().UnixNano() - begin)/1000000000))
}
func main(){
defer func(){
if err := recover(); err != nil {
fmt.Println(err)
fmt.Println(string(debug.Stack()))
time.Sleep(time.Duration(3000)*time.Second)
}
}()
fmt.Println("start...")
//huCards2JSON()
benchmark()
fmt.Println("end...")
time.Sleep(time.Duration(3000)*time.Second)
}
结论: 查表法效率稍高。拆解法简单方便,不需要预先生成表,但不稳定:不同的牌型,计算胡牌时消耗时间相差可能很大。查表法需要预先生成表,计算胡牌比较稳定