├── .gitignore ├── .idea └── vcs.xml ├── README.md ├── analyzer_landlord.go ├── cardname_recorder.go ├── doc.go ├── factory.go ├── setchecker_landlord_test.go ├── setchecker_lanlord.go ├── setinfo.go ├── sorts.go └── sorts_test.go /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # pokergame 2 | 多种经典扑克牌游戏的基础规则实现,包括牌型检测,牌型比较,记牌器,牌型分析器等 3 | -------------------------------------------------------------------------------- /analyzer_landlord.go: -------------------------------------------------------------------------------- 1 | package pokergame 2 | 3 | import ( 4 | "github.com/wqtapp/poker" 5 | "sync" 6 | ) 7 | 8 | //定义玩家的扑克牌分析器map的索引为poker的value,value为改值得扑克牌在玩家牌中的索引 9 | type landLordAnalyzer struct{ 10 | sync.RWMutex 11 | dic map[int]poker.PokerSet 12 | } 13 | 14 | //根据给定的扑克集初始化分析器 15 | func (ana *landLordAnalyzer) InitAnalyzer(){ 16 | ana.dic = make(map[int]poker.PokerSet) 17 | ana.dic[poker.CARD_VALUE_THREE] = poker.PokerSet{} 18 | ana.dic[poker.CARD_VALUE_FOUR] = poker.PokerSet{} 19 | ana.dic[poker.CARD_VALUE_FIVE] = poker.PokerSet{} 20 | ana.dic[poker.CARD_VALUE_SIX] = poker.PokerSet{} 21 | ana.dic[poker.CARD_VALUE_SEVEN] = poker.PokerSet{} 22 | ana.dic[poker.CARD_VALUE_EIGHT] = poker.PokerSet{} 23 | ana.dic[poker.CARD_VALUE_NINE] = poker.PokerSet{} 24 | ana.dic[poker.CARD_VALUE_TEN] = poker.PokerSet{} 25 | ana.dic[poker.CARD_VALUE_JACK] = poker.PokerSet{} 26 | ana.dic[poker.CARD_VALUE_QUEEN] = poker.PokerSet{} 27 | ana.dic[poker.CARD_VALUE_KING] = poker.PokerSet{} 28 | ana.dic[poker.CARD_VALUE_ACE] = poker.PokerSet{} 29 | ana.dic[poker.CARD_VALUE_TWO] = poker.PokerSet{} 30 | ana.dic[poker.CARD_VALUE_BLACK_JOKER] = poker.PokerSet{} 31 | ana.dic[poker.CARD_VALUE_RED_JOKER] = poker.PokerSet{} 32 | } 33 | //根据给定的扑克集更新记牌器,出牌时调用 34 | func (ana *landLordAnalyzer) RemovePokerSet(pokers poker.PokerSet){ 35 | ana.Lock() 36 | defer ana.Unlock() 37 | pokers.DoOnEachPokerCard(func(index int,card *poker.PokerCard){ 38 | ana.dic[card.GetValue()],_ = ana.dic[card.GetValue()].DelPokers(poker.PokerSet{card}) 39 | }) 40 | } 41 | 42 | func (ana *landLordAnalyzer) AddPokerSet(pokers poker.PokerSet){ 43 | ana.Lock() 44 | defer ana.Unlock() 45 | pokers.DoOnEachPokerCard(func(index int,card *poker.PokerCard){ 46 | ana.dic[card.GetValue()] = ana.dic[card.GetValue()].AddPokers(poker.PokerSet{card}) 47 | }) 48 | } 49 | 50 | func (ana *landLordAnalyzer) GetMinPlayableCards() poker.PokerSet{ 51 | ana.Lock() 52 | defer ana.Unlock() 53 | for i:= poker.CARD_VALUE_THREE;i<=poker.CARD_VALUE_RED_JOKER;i++{ 54 | set,_ := ana.dic[i] 55 | if set.CountCards() > 0{ 56 | return set 57 | } 58 | } 59 | return poker.PokerSet{} 60 | } 61 | //根据最后一次出牌的牌型信息,返回可出的扑克集 62 | func (ana *landLordAnalyzer) GetUseableCards(setType *SetInfo) []poker.PokerSet{ 63 | ana.Lock() 64 | defer ana.Unlock() 65 | 66 | var useableSets []poker.PokerSet 67 | 68 | switch setType.setType { 69 | case LANDLORD_SET_TYPE_SINGLE: 70 | useableSets = ana.getSingleValueSet(1,setType.GetMinValue()) 71 | case LANDLORD_SET_TYPE_DRAGON: 72 | useableSets = ana.getMultiValueSet(1,setType.GetMinValue(),setType.GetMaxValue()) 73 | case LANDLORD_SET_TYPE_PAIR: 74 | useableSets = ana.getSingleValueSet(2,setType.GetMinValue()) 75 | case LANDLORD_SET_TYPE_MULIT_PAIRS: 76 | useableSets = ana.getMultiValueSet(2,setType.GetMinValue(),setType.GetMaxValue()) 77 | case LANDLORD_SET_TYPE_THREE: 78 | useableSets = ana.getSingleValueSet(3,setType.GetMinValue()) 79 | case LANDLORD_SET_TYPE_THREE_PLUS_ONE: 80 | useableSets = ana.getSingleValueSet(3,setType.GetMinValue()) 81 | for i,tempset := range useableSets{ 82 | tempsetPlus := ana.getPlusSet(1,1,tempset) 83 | if tempsetPlus.CountCards() >0 { 84 | useableSets[i] = tempset.AddPokers(tempsetPlus) 85 | }else{//没有牌可以带,将之前的主牌移除可出牌集合 86 | useableSets[i] = nil 87 | } 88 | } 89 | case LANDLORD_SET_TYPE_THREE_PLUS_TWO: 90 | useableSets = ana.getSingleValueSet(3,setType.GetMinValue()) 91 | for i,tempset := range useableSets{ 92 | tempsetPlus := ana.getPlusSet(2,1,tempset) 93 | if tempsetPlus.CountCards() >0 { 94 | useableSets[i] = tempset.AddPokers(tempsetPlus) 95 | }else{//没有牌可以带,将之前的主牌移除可出牌集合 96 | useableSets[i] = nil 97 | } 98 | } 99 | case LANDLORD_SET_TYPE_MULITY_THREE: 100 | useableSets = ana.getMultiValueSet(3,setType.GetMinValue(),setType.GetMaxValue()) 101 | case LANDLORD_SET_TYPE_MULITY_THREE_PLUS_ONE: 102 | useableSets = ana.getMultiValueSet(3,setType.GetMinValue(),setType.GetMaxValue()) 103 | for i,tempset := range useableSets{ 104 | tempsetPlus := ana.getPlusSet(1,setType.GetRangeWidth(),tempset) 105 | if tempsetPlus.CountCards() >0 { 106 | useableSets[i] = tempset.AddPokers(tempsetPlus) 107 | }else{//没有牌可以带,将之前的主牌移除可出牌集合 108 | useableSets[i] = nil 109 | } 110 | } 111 | case LANDLORD_SET_TYPE_MULITY_THREE_PLUS_TWO: 112 | useableSets = ana.getMultiValueSet(3,setType.GetMinValue(),setType.GetMaxValue()) 113 | for i,tempset := range useableSets{ 114 | tempsetPlus := ana.getPlusSet(2,setType.GetRangeWidth(),tempset) 115 | if tempsetPlus.CountCards() >0 { 116 | useableSets[i] = tempset.AddPokers(tempsetPlus) 117 | }else{//没有牌可以带,将之前的主牌移除可出牌集合 118 | useableSets[i] = nil 119 | } 120 | } 121 | case LANDLORD_SET_TYPE_FOUR_PLUS_TWO: 122 | useableSets = ana.getSingleValueSet(4,setType.GetMinValue()) 123 | for i,tempset := range useableSets{ 124 | //带两个单牌 125 | tempsetPlus := ana.getPlusSet(1,2,tempset) 126 | if tempsetPlus.CountCards() >0 { 127 | useableSets[i] = tempset.AddPokers(tempsetPlus) 128 | }else{ 129 | //带一对牌,看做两个单牌 130 | tempsetPlus := ana.getPlusSet(2,1,tempset) 131 | if tempsetPlus.CountCards() >0 { 132 | useableSets[i] = tempset.AddPokers(tempsetPlus) 133 | }else{//没有牌可以带,将之前的主牌移除可出牌集合 134 | useableSets[i] = nil 135 | } 136 | } 137 | } 138 | case LANDLORD_SET_TYPE_FOUR_PLUS_FOUR: 139 | useableSets = ana.getSingleValueSet(4,setType.GetMinValue()) 140 | for i,tempset := range useableSets{ 141 | tempsetPlus := ana.getPlusSet(2,2,tempset) 142 | if tempsetPlus.CountCards() >0 { 143 | useableSets[i] = tempset.AddPokers(tempsetPlus) 144 | }else{//没有牌可以带,将之前的主牌移除可出牌集合 145 | useableSets[i] = nil 146 | } 147 | } 148 | case LANDLORD_SET_TYPE_MULITY_FOUR: 149 | useableSets = ana.getMultiValueSet(4,setType.GetMinValue(),setType.GetMaxValue()) 150 | case LANDLORD_SET_TYPE_MULITY_FOUR_PLUS_TWO: 151 | useableSets = ana.getMultiValueSet(4,setType.GetMinValue(),setType.GetMaxValue()) 152 | for i,tempset := range useableSets{ 153 | //带两个单牌 154 | tempsetPlus := ana.getPlusSet(1,2*setType.GetRangeWidth(),tempset) 155 | if tempsetPlus.CountCards() >0 { 156 | useableSets[i] = tempset.AddPokers(tempsetPlus) 157 | }else{ 158 | //带一对牌,看做两个单牌 159 | tempsetPlus := ana.getPlusSet(2,setType.GetRangeWidth(),tempset) 160 | if tempsetPlus.CountCards() >0 { 161 | useableSets[i] = tempset.AddPokers(tempsetPlus) 162 | }else{//没有牌可以带,将之前的主牌移除可出牌集合 163 | useableSets[i] = nil 164 | } 165 | } 166 | } 167 | case LANDLORD_SET_TYPE_MULITY_FOUR_PLUS_FOUR: 168 | useableSets = ana.getMultiValueSet(4,setType.GetMinValue(),setType.GetMaxValue()) 169 | for i,tempset := range useableSets{ 170 | tempsetPlus := ana.getPlusSet(2,2*setType.GetRangeWidth(),tempset) 171 | if tempsetPlus.CountCards() >0 { 172 | useableSets[i] = tempset.AddPokers(tempsetPlus) 173 | }else{//没有牌可以带,将之前的主牌移除可出牌集合 174 | useableSets[i] = nil 175 | } 176 | } 177 | case LANDLORD_SET_TYPE_COMMON_BOMB: 178 | useableSets = ana.getSingleValueSet(4,setType.GetMinValue()) 179 | case LANDLORD_SET_TYPE_JOKER_BOMB: 180 | useableSets = []poker.PokerSet{} 181 | default: 182 | useableSets = []poker.PokerSet{} 183 | } 184 | //去掉nil元素 185 | newUseableSets := []poker.PokerSet{} 186 | for _,sets := range useableSets{ 187 | if sets != nil{ 188 | newUseableSets = append(newUseableSets,sets) 189 | } 190 | } 191 | //上一次出牌不是炸弹,则直接将炸弹加入可出的排中 192 | if setType.setType != LANDLORD_SET_TYPE_COMMON_BOMB && setType.setType != LANDLORD_SET_TYPE_JOKER_BOMB { 193 | //王炸 194 | jokerBombSet := ana.getJokerBomb() 195 | if jokerBombSet.CountCards() > 0{ 196 | newUseableSets = append(newUseableSets,jokerBombSet) 197 | } 198 | //普通炸弹 199 | for _,tempSet := range ana.getSingleValueSet(4,-1){ 200 | if tempSet.CountCards() > 0{ 201 | newUseableSets = append(newUseableSets,tempSet) 202 | } 203 | } 204 | } 205 | return newUseableSets 206 | } 207 | //获取单值牌组成的扑克集的切片,单排对牌三牌四排等等 208 | //count表示单值牌的张数 209 | //minValue表示上家出牌的最小的牌的大小 210 | func (ana *landLordAnalyzer) getSingleValueSet(count int,minValue int) []poker.PokerSet{ 211 | sets := []poker.PokerSet{} 212 | se := poker.NewPokerSet() 213 | //先不拆牌的情况下查找 214 | for i:=minValue+1;i<= poker.CARD_VALUE_RED_JOKER;i++{ 215 | if ana.dic[i].CountCards() == count { 216 | se = se.AddPokers(ana.dic[i]) 217 | sets = append(sets,se) 218 | se = poker.NewPokerSet() 219 | } 220 | } 221 | //不拆牌的情况下找不到可出的牌,再考虑拆牌的情况 222 | if len(sets) == 0{ 223 | for i:=minValue+1;i<= poker.CARD_VALUE_RED_JOKER;i++{ 224 | if ana.dic[i].CountCards() > count { 225 | se = se.AddPokers(ana.dic[i][:count]) 226 | sets = append(sets,se) 227 | se = poker.NewPokerSet() 228 | } 229 | } 230 | } 231 | return sets 232 | } 233 | //获取多种不同值组成的扑克集的切片,2连3连4连5连等 234 | func (ana *landLordAnalyzer) getMultiValueSet(count int,minValue int,maxValue int) []poker.PokerSet{ 235 | sets := []poker.PokerSet{} 236 | se := poker.NewPokerSet() 237 | valueRange := maxValue-minValue+1 238 | //先考虑不拆拍的情况 239 | for i:=minValue+1;i<= poker.CARD_VALUE_TWO-valueRange;i++{ 240 | for j:=i;j count { 258 | se = se.AddPokers(ana.dic[j][:count]) 259 | } 260 | } 261 | //该范围内连续的牌的张数符合要求 262 | if se.CountCards() == valueRange*count{ 263 | sets = append(sets,se) 264 | se = poker.NewPokerSet() 265 | }else{ 266 | se = poker.NewPokerSet() 267 | } 268 | } 269 | } 270 | 271 | return sets 272 | } 273 | //获取附牌,比如三带一中的一,四带二中二,只获取一种可能即可 274 | //不拆牌为第一原则,可能会带出去大牌 275 | //num张数count系列数exceptset不能包含在内的扑克集 276 | func (ana *landLordAnalyzer) getPlusSet(num int,count int,exceptSet poker.PokerSet) poker.PokerSet{ 277 | resSet := poker.NewPokerSet() 278 | //第一原则不拆牌原则 279 | for i:= poker.CARD_VALUE_THREE;i<= poker.CARD_VALUE_RED_JOKER;i++{ 280 | if ana.dic[i].CountCards() == num{ 281 | if !ana.dic[i][:num].HasSameValueCard(exceptSet) { 282 | resSet = resSet.AddPokers(ana.dic[i]) 283 | } 284 | } 285 | if resSet.CountCards() == num*count{ 286 | return resSet 287 | } 288 | } 289 | //不拆牌找不到则,考虑拆牌 290 | if resSet.CountCards() == 0{ 291 | for i:= poker.CARD_VALUE_THREE;i<= poker.CARD_VALUE_RED_JOKER;i++{ 292 | if ana.dic[i].CountCards() > num{ 293 | if !ana.dic[i][:num].HasSameValueCard(exceptSet) { 294 | resSet = resSet.AddPokers(ana.dic[i][:num]) 295 | } 296 | } 297 | if resSet.CountCards() == num*count{ 298 | return resSet 299 | } 300 | } 301 | } 302 | 303 | return poker.PokerSet{} 304 | } 305 | func (ana *landLordAnalyzer) getJokerBomb() poker.PokerSet{ 306 | resSet := poker.NewPokerSet() 307 | for i:= poker.CARD_VALUE_BLACK_JOKER;i<= poker.CARD_VALUE_RED_JOKER;i++ { 308 | if ana.dic[i].CountCards() > 0 { 309 | resSet = resSet.AddPokers(ana.dic[i]) 310 | } 311 | } 312 | if resSet.CountCards() > 1{ 313 | return resSet 314 | }else{ 315 | return poker.NewPokerSet() 316 | } 317 | } 318 | 319 | 320 | -------------------------------------------------------------------------------- /cardname_recorder.go: -------------------------------------------------------------------------------- 1 | package pokergame 2 | 3 | import ( 4 | "strconv" 5 | "github.com/wqtapp/poker" 6 | "sync" 7 | ) 8 | 9 | 10 | //记牌器,记录玩家之外带各牌待出的数量 11 | type cardNameRecorder struct { 12 | sync.RWMutex 13 | dic map[string]int 14 | } 15 | 16 | //初始化玩家记牌器,发牌后调用 17 | func (re *cardNameRecorder)InitRecorder(){ 18 | re.dic = make(map[string]int) 19 | re.dic[poker.CARD_SYMBOL_THREE] = 0 20 | re.dic[poker.CARD_SYMBOL_FOUR] = 0 21 | re.dic[poker.CARD_SYMBOL_FIVE] = 0 22 | re.dic[poker.CARD_SYMBOL_SIX] = 0 23 | re.dic[poker.CARD_SYMBOL_SEVEN] = 0 24 | re.dic[poker.CARD_SYMBOL_EIGHT] = 0 25 | re.dic[poker.CARD_SYMBOL_NINE] = 0 26 | re.dic[poker.CARD_SYMBOL_TEN] = 0 27 | re.dic[poker.CARD_SYMBOL_JACK] = 0 28 | re.dic[poker.CARD_SYMBOL_QUEEN] = 0 29 | re.dic[poker.CARD_SYMBOL_KING] = 0 30 | re.dic[poker.CARD_SYMBOL_ACE] = 0 31 | re.dic[poker.CARD_SYMBOL_TWO] = 0 32 | re.dic[poker.CARD_SYMBOL_BLACK_JOKER] = 0 33 | re.dic[poker.CARD_SYMBOL_RED_JOKER] = 0 34 | } 35 | //增加记录器 36 | func (re *cardNameRecorder)AddPokerSet(playerPokers ...poker.PokerSet){ 37 | re.Lock() 38 | defer re.Unlock() 39 | for _,pokerSet := range playerPokers{ 40 | pokerSet.DoOnEachPokerCard(func(index int, card *poker.PokerCard){ 41 | re.dic[card.GetCardName()]++ 42 | }) 43 | } 44 | 45 | } 46 | //更新玩家记牌器,玩家出牌后调用 47 | func (re *cardNameRecorder)RemovePokerSet(cards poker.PokerSet){ 48 | re.Lock() 49 | defer re.Unlock() 50 | cards.DoOnEachPokerCard(func(index int,card *poker.PokerCard){ 51 | re.dic[card.GetCardName()]-- 52 | }) 53 | } 54 | //根据牌的顺序从大到小排序的记牌器json对象 55 | func (re *cardNameRecorder) SequenceJsonEncode() string{ 56 | re.Lock() 57 | defer re.Unlock() 58 | jsonString := "" 59 | jsonString += "{" 60 | jsonString += "'"+poker.CARD_SYMBOL_RED_JOKER+"'"+":"+strconv.Itoa(re.getPokerNum(poker.CARD_SYMBOL_RED_JOKER)) 61 | jsonString += "," 62 | jsonString += "'"+poker.CARD_SYMBOL_BLACK_JOKER+"'"+":"+strconv.Itoa(re.getPokerNum(poker.CARD_SYMBOL_BLACK_JOKER)) 63 | jsonString += "," 64 | jsonString += "'"+poker.CARD_SYMBOL_TWO+"'"+":"+strconv.Itoa(re.getPokerNum(poker.CARD_SYMBOL_TWO)) 65 | jsonString += "," 66 | jsonString += "'"+poker.CARD_SYMBOL_ACE+"'"+":"+strconv.Itoa(re.getPokerNum(poker.CARD_SYMBOL_ACE)) 67 | jsonString += "," 68 | jsonString += "'"+poker.CARD_SYMBOL_KING+"'"+":"+strconv.Itoa(re.getPokerNum(poker.CARD_SYMBOL_KING)) 69 | jsonString += "," 70 | jsonString += "'"+poker.CARD_SYMBOL_QUEEN+"'"+":"+strconv.Itoa(re.getPokerNum(poker.CARD_SYMBOL_QUEEN)) 71 | jsonString += "," 72 | jsonString += "'"+poker.CARD_SYMBOL_JACK+"'"+":"+strconv.Itoa(re.getPokerNum(poker.CARD_SYMBOL_JACK)) 73 | jsonString += "," 74 | jsonString += "'"+poker.CARD_SYMBOL_TEN+"'"+":"+strconv.Itoa(re.getPokerNum(poker.CARD_SYMBOL_TEN)) 75 | jsonString += "," 76 | jsonString += "'"+poker.CARD_SYMBOL_NINE+"'"+":"+strconv.Itoa(re.getPokerNum(poker.CARD_SYMBOL_NINE)) 77 | jsonString += "," 78 | jsonString += "'"+poker.CARD_SYMBOL_EIGHT+"'"+":"+strconv.Itoa(re.getPokerNum(poker.CARD_SYMBOL_EIGHT)) 79 | jsonString += "," 80 | jsonString += "'"+poker.CARD_SYMBOL_SEVEN+"'"+":"+strconv.Itoa(re.getPokerNum(poker.CARD_SYMBOL_SEVEN)) 81 | jsonString += "," 82 | jsonString += "'"+poker.CARD_SYMBOL_SIX+"'"+":"+strconv.Itoa(re.getPokerNum(poker.CARD_SYMBOL_SIX)) 83 | jsonString += "," 84 | jsonString += "'"+poker.CARD_SYMBOL_FIVE+"'"+":"+strconv.Itoa(re.getPokerNum(poker.CARD_SYMBOL_FIVE)) 85 | jsonString += "," 86 | jsonString += "'"+poker.CARD_SYMBOL_FOUR+"'"+":"+strconv.Itoa(re.getPokerNum(poker.CARD_SYMBOL_FOUR)) 87 | jsonString += "," 88 | jsonString += "'"+poker.CARD_SYMBOL_THREE+"'"+":"+strconv.Itoa(re.getPokerNum(poker.CARD_SYMBOL_THREE)) 89 | jsonString += "}" 90 | return jsonString 91 | } 92 | 93 | func (re *cardNameRecorder) getPokerNum(key string) int{ 94 | num,ok := re.dic[key] 95 | if ok { 96 | return num 97 | }else{ 98 | return 0 99 | } 100 | } 101 | 102 | 103 | -------------------------------------------------------------------------------- /doc.go: -------------------------------------------------------------------------------- 1 | package pokergame 2 | 3 | /* 4 | 该包实现一些常用游戏的牌型检测,记牌器,牌型分析器等功能 5 | 1.该包包含扑克牌、扑克集、扑克分析器、扑克记录器、扑克集分析器等概念的定义和实现 6 | 2.根据不同游戏类型,比如斗地主、保皇、升级、扎金花等等扑克牌游戏相关概念的实现,使用工厂模式产生对应的实体 7 | */ 8 | -------------------------------------------------------------------------------- /factory.go: -------------------------------------------------------------------------------- 1 | package pokergame 2 | 3 | import ( 4 | "github.com/wqtapp/poker" 5 | ) 6 | 7 | //定义扑克牌游戏的类型,用于获取适用于不同扑克牌的set和analayzer 8 | const ( 9 | GAME_OF_LANDLORD = iota 10 | GAME_OF_ROYALIST 11 | GAME_OF_ZHAJINHUA 12 | GAME_OF_SHENGJI 13 | ) 14 | 15 | type IRecorder interface { 16 | InitRecorder() 17 | AddPokerSet(playerPokers ...poker.PokerSet) 18 | RemovePokerSet(cards poker.PokerSet) 19 | SequenceJsonEncode() string 20 | } 21 | 22 | type IAnalyzer interface { 23 | InitAnalyzer() 24 | AddPokerSet(pokers poker.PokerSet) 25 | RemovePokerSet(pokers poker.PokerSet) 26 | GetMinPlayableCards() poker.PokerSet 27 | GetUseableCards(setType *SetInfo) []poker.PokerSet 28 | 29 | } 30 | 31 | type ISetChecker interface { 32 | GetSetInfo(set poker.PokerSet) (*SetInfo,error) 33 | } 34 | 35 | //获取适用于某一种游戏的扑克集 36 | func NewSetChecker(gameType int) ISetChecker{ 37 | switch gameType { 38 | case GAME_OF_LANDLORD: 39 | landLordChecker := landLordChecker{} 40 | return &landLordChecker 41 | case GAME_OF_ROYALIST: 42 | case GAME_OF_SHENGJI: 43 | case GAME_OF_ZHAJINHUA: 44 | default: 45 | return nil 46 | } 47 | return nil 48 | } 49 | //获取适用于某一种游戏的扑克分析器 50 | func NewAnalyzer(gameType int) IAnalyzer{ 51 | switch gameType { 52 | case GAME_OF_LANDLORD: 53 | analyzer := landLordAnalyzer{} 54 | analyzer.InitAnalyzer() 55 | return &analyzer 56 | case GAME_OF_ROYALIST: 57 | case GAME_OF_SHENGJI: 58 | case GAME_OF_ZHAJINHUA: 59 | default: 60 | return nil 61 | } 62 | return nil 63 | } 64 | //获取适用于某一种游戏的扑克记录器 65 | func NewRecorder(gameType int) IRecorder{ 66 | switch gameType { 67 | case GAME_OF_LANDLORD: 68 | recorder := cardNameRecorder{} 69 | recorder.InitRecorder() 70 | return &recorder 71 | case GAME_OF_ROYALIST: 72 | case GAME_OF_SHENGJI: 73 | case GAME_OF_ZHAJINHUA: 74 | default: 75 | return nil 76 | } 77 | return nil 78 | } 79 | 80 | 81 | -------------------------------------------------------------------------------- /setchecker_landlord_test.go: -------------------------------------------------------------------------------- 1 | package pokergame 2 | 3 | import ( 4 | "testing" 5 | "reflect" 6 | "strconv" 7 | "github.com/wqtapp/poker" 8 | "fmt" 9 | ) 10 | 11 | var dec poker.PokerDeck 12 | 13 | func init(){ 14 | dec = poker.CreateDeck() 15 | } 16 | 17 | type Check struct{ 18 | setStr []string //牌型字符串 19 | isTrue bool //是否检测的类型 20 | setType int //扑克集类型 21 | } 22 | 23 | func checkBool(t *testing.T,c []Check,funcName string){ 24 | for i := range c{ 25 | set := getPokerset(c[i].setStr) 26 | setInfo := reflect.ValueOf(&landLordChecker{}) 27 | method := setInfo.MethodByName(funcName) 28 | if method.String() == ""{ 29 | t.Error("no this func "+funcName) 30 | }else{ 31 | params := make([]reflect.Value, 1) // 参数 32 | params[0] = reflect.ValueOf(set) // 参数设置为20 33 | res := method.Call(params) 34 | if res[0].Interface().(bool) != c[i].isTrue{ 35 | t.Error(funcName+strconv.Itoa(i)) 36 | } 37 | } 38 | } 39 | } 40 | 41 | func checkBoolWithType(t *testing.T,c []Check,funcName string){ 42 | for i := range c{ 43 | set := getPokerset(c[i].setStr) 44 | setInfo := reflect.ValueOf(landLordChecker{}) 45 | fmt.Println(setInfo.NumMethod()) 46 | return 47 | method := setInfo.MethodByName(funcName) 48 | if method.String() == ""{ 49 | t.Error("no this func "+funcName) 50 | }else{ 51 | params := make([]reflect.Value, 1) // 参数 52 | params[0] = reflect.ValueOf(set) 53 | res := method.Call(params) 54 | tempBool := false 55 | if res[1].Interface() == nil{ 56 | tempBool = true 57 | } 58 | 59 | if tempBool != c[i].isTrue { 60 | t.Error(funcName+" err"+strconv.Itoa(i)) 61 | }else{ 62 | 63 | if !c[i].isTrue{ 64 | continue 65 | } 66 | 67 | if res[0].Interface() != nil{ 68 | setTypeInfo,ok := res[0].Interface().(*SetInfo) 69 | if ok{ 70 | if setTypeInfo == nil || setTypeInfo.setType != c[i].setType{ 71 | if setTypeInfo == nil{ 72 | t.Error(funcName+" typeInfo nil"+strconv.Itoa(i)) 73 | }else{ 74 | t.Error(funcName+" typeInfo "+strconv.Itoa(setTypeInfo.setType)+" "+strconv.Itoa(i)) 75 | } 76 | } 77 | }else{ 78 | t.Error(funcName+" typeInfo "+strconv.Itoa(setTypeInfo.setType)+" "+strconv.Itoa(i)) 79 | } 80 | }else{ 81 | t.Error(funcName+" typeInfo result nil"+strconv.Itoa(i)) 82 | } 83 | } 84 | } 85 | } 86 | } 87 | 88 | //根据传入的字符数组,生成相应的扑克集,便于写测试用例 89 | //此处不区分花色 90 | func getPokerset(setString []string) poker.PokerSet{ 91 | set := poker.PokerSet{} 92 | for _,name := range setString{ 93 | for i,card := range dec.GetAllCards(){ 94 | if card.GetCardName() == name{ 95 | set = append(set,dec.GetCard(i)) 96 | break 97 | } 98 | } 99 | } 100 | return set 101 | } 102 | //先测试生成扑克集的正确性 103 | func TestPokerSet_GetPokerset(t *testing.T){ 104 | test := []string{"3","4","5"} 105 | set := getPokerset(test) 106 | if set[0].GetCardName() != test[0]{ 107 | t.Error("pokerset creator err") 108 | } 109 | 110 | if set[1].GetCardName() != test[1]{ 111 | t.Error("pokerset creator err") 112 | } 113 | 114 | if set[2].GetCardName() != test[2]{ 115 | t.Error("pokerset creator err") 116 | } 117 | 118 | } 119 | 120 | func TestPokerSet_GetSetTypeInfo(t *testing.T) { 121 | checks := []Check{ 122 | {[]string{"3"}, true, LANDLORD_SET_TYPE_SINGLE}, 123 | {[]string{"3", "3"}, true, LANDLORD_SET_TYPE_PAIR}, 124 | {[]string{"3", "3", "4", "4", "5", "5"}, true, LANDLORD_SET_TYPE_MULIT_PAIRS}, 125 | {[]string{"3", "3", "3"}, true, LANDLORD_SET_TYPE_THREE}, 126 | {[]string{"3", "3", "3", "4"}, true, LANDLORD_SET_TYPE_THREE_PLUS_ONE}, 127 | {[]string{"3", "3", "3", "4", "4"}, true, LANDLORD_SET_TYPE_THREE_PLUS_TWO}, 128 | {[]string{"3", "3", "3", "4", "4", "4"}, true, LANDLORD_SET_TYPE_MULITY_THREE}, 129 | {[]string{"3", "3", "3", "4", "4", "4", "5", "6"}, true, LANDLORD_SET_TYPE_MULITY_THREE_PLUS_ONE}, 130 | {[]string{"3", "3", "3", "4", "4", "4", "5", "5","5","7","7","7"}, true, LANDLORD_SET_TYPE_MULITY_THREE_PLUS_ONE}, 131 | {[]string{"3", "3", "3", "4", "4", "4", "5", "6", "5", "6"}, true, LANDLORD_SET_TYPE_MULITY_THREE_PLUS_TWO}, 132 | {[]string{"3", "3", "3", "4", "4", "4", "5", "5", "5", "5"}, true, LANDLORD_SET_TYPE_MULITY_THREE_PLUS_TWO}, 133 | 134 | {[]string{"3", "3", "3", "3"}, true, LANDLORD_SET_TYPE_COMMON_BOMB}, 135 | {[]string{"3", "3", "3", "3", "4", "5"}, true, LANDLORD_SET_TYPE_FOUR_PLUS_TWO}, 136 | {[]string{"3", "3", "3", "3", "4", "4", "5", "5"}, true, LANDLORD_SET_TYPE_FOUR_PLUS_FOUR}, 137 | {[]string{"3", "3", "3", "3", "5", "5", "5", "5"}, true, LANDLORD_SET_TYPE_FOUR_PLUS_FOUR}, 138 | {[]string{"3", "3", "3", "3", "4", "4", "4", "4"}, true, LANDLORD_SET_TYPE_MULITY_FOUR}, 139 | {[]string{"3", "3", "3", "3", "4", "4", "4", "4", "5", "6", "7", "8"}, true, LANDLORD_SET_TYPE_MULITY_FOUR_PLUS_TWO}, 140 | {[]string{"3", "3", "3", "3", "4", "4", "4", "4", "5", "5", "7", "8"}, true, LANDLORD_SET_TYPE_MULITY_FOUR_PLUS_TWO}, 141 | {[]string{"3", "3", "3", "3", "4", "4", "4", "4", "5", "5", "6", "6", "7", "7", "8", "8"}, true, LANDLORD_SET_TYPE_MULITY_FOUR_PLUS_FOUR}, 142 | 143 | {[]string{"3", "4", "5", "6", "7", "8"}, true, LANDLORD_SET_TYPE_DRAGON}, 144 | {[]string{"BlackJoker", "RedJoker"}, true, LANDLORD_SET_TYPE_JOKER_BOMB}, 145 | } 146 | 147 | checkBoolWithType(t, checks, "GetSetInfo") 148 | } -------------------------------------------------------------------------------- /setchecker_lanlord.go: -------------------------------------------------------------------------------- 1 | package pokergame 2 | 3 | import 4 | ( 5 | "errors" 6 | "github.com/wqtapp/poker" 7 | ) 8 | 9 | type landLordChecker struct { 10 | 11 | } 12 | func (self landLordChecker) GetSetInfo(set poker.PokerSet) (*SetInfo,error) { 13 | switch set.CountCards() { 14 | case 0: 15 | return nil,errors.New("玩家出牌为空") 16 | //单张 17 | case 1: 18 | return NewSetInfo(LANDLORD_SET_TYPE_SINGLE,set[0].GetValue(),set[0].GetValue()),nil 19 | //对子或者王炸 20 | case 2: 21 | if self.isPair(set){ 22 | return NewSetInfo(LANDLORD_SET_TYPE_PAIR,set[0].GetValue(),set[0].GetValue()),nil 23 | } 24 | 25 | if self.isJokerBomb(set){ 26 | return NewSetInfo(LANDLORD_SET_TYPE_JOKER_BOMB,set[0].GetValue(),set[1].GetValue()),nil 27 | } 28 | 29 | return nil,errors.New("牌型不符合规则") 30 | //三张 31 | case 3: 32 | return self.checkThreePlus(set) 33 | //炸弹或三带一 34 | case 4: 35 | if self.isCommonBomb(set){ 36 | return NewSetInfo(LANDLORD_SET_TYPE_COMMON_BOMB,set[0].GetValue(),set[0].GetValue()),nil 37 | } 38 | return self.checkThreePlus(set) 39 | //三带二或者一条龙 40 | case 5: 41 | if self.isDragon(set){ 42 | return NewSetInfo(LANDLORD_SET_TYPE_DRAGON,set[0].GetValue(),set[set.CountCards()-1].GetValue()),nil 43 | } 44 | return self.checkThreePlus(set) 45 | //一条龙,或者四带二,或者四带二对 46 | default: 47 | if self.isDragon(set){ 48 | return NewSetInfo(LANDLORD_SET_TYPE_DRAGON,set[0].GetValue(),set[set.CountCards()-1].GetValue()),nil 49 | } 50 | 51 | if self.isMultiPair(set){ 52 | return NewSetInfo(LANDLORD_SET_TYPE_MULIT_PAIRS,set[0].GetValue(),set[set.CountCards()-1].GetValue()),nil 53 | } 54 | 55 | if cardsType,err := self.checkFourPlus(set);err == nil{ 56 | return cardsType,err 57 | }else if cardsType,err := self.checkMultiFourPlus(set);err == nil{ 58 | return cardsType,err 59 | }else{ 60 | return self.checkMultiThreePlus(set) 61 | } 62 | } 63 | } 64 | 65 | func (self landLordChecker) isPair(set poker.PokerSet) bool{ 66 | if set.CountCards() != 2 { 67 | return false 68 | } 69 | 70 | if(set[0].GetValue() == set[1].GetValue()){ 71 | return true 72 | } 73 | return false 74 | } 75 | 76 | func (self landLordChecker) isMultiPair(set poker.PokerSet) bool{ 77 | if set.CountCards()%2 != 0 || len(set) < 6 { 78 | return false 79 | } 80 | 81 | set.SortAsc() 82 | //2和王不能作为连对出牌 83 | if set[set.CountCards()-1].GetValue() >= poker.CARD_VALUE_TWO{ 84 | return false 85 | } 86 | 87 | currValue := -1 88 | 89 | for i,card := range set{ 90 | if i == 0 { 91 | currValue = card.GetValue() 92 | }else{ 93 | if i % 2 == 1{ 94 | if card.GetValue() != currValue{ 95 | return false 96 | } 97 | }else{ 98 | if card.GetValue() == currValue+1{ 99 | currValue=card.GetValue() 100 | }else{ 101 | return false 102 | } 103 | } 104 | } 105 | } 106 | return true 107 | } 108 | 109 | func (self landLordChecker) isJokerBomb(set poker.PokerSet) bool{ 110 | if set.CountCards() != 2{ 111 | return false 112 | } 113 | set.SortAsc() 114 | if(set[0].GetValue() == poker.CARD_VALUE_BLACK_JOKER && set[1].GetValue() == poker.CARD_VALUE_RED_JOKER){ 115 | return true 116 | }else{ 117 | 118 | } 119 | return false 120 | } 121 | 122 | func (self landLordChecker) isCommonBomb(set poker.PokerSet) bool{ 123 | if set.CountCards() != 4{ 124 | return false 125 | } 126 | if set[0].GetValue() == set[1].GetValue() && set[2].GetValue() == set[3].GetValue() && 127 | set[0].GetValue() == set[2].GetValue(){ 128 | return true 129 | }else{ 130 | return false 131 | } 132 | } 133 | 134 | func (self landLordChecker) isDragon(set poker.PokerSet) bool{ 135 | if len(set) < 5 { 136 | return false 137 | } 138 | 139 | set.SortAsc() 140 | //2和王不能参与顺子出牌 141 | if set[set.CountCards()-1].GetValue() >= poker.CARD_VALUE_TWO{ 142 | return false 143 | } 144 | 145 | tempValue := -1 146 | for i,card := range set{ 147 | if i == 0 { 148 | tempValue = card.GetValue() 149 | }else{ 150 | if card.GetValue() == tempValue+1{ 151 | tempValue = card.GetValue() 152 | }else{ 153 | return false 154 | } 155 | } 156 | } 157 | return true 158 | } 159 | 160 | func (self landLordChecker) checkThreePlus(set poker.PokerSet) (*SetInfo,error){ 161 | pokersNum := set.CountCards() 162 | if pokersNum < 3 || pokersNum >5{ 163 | return nil,errors.New("不是三带牌") 164 | } 165 | 166 | set.SortAsc() 167 | cardNum := set.AnalyzeEachCardValueNum() 168 | cardNumCount := len(cardNum) 169 | if pokersNum == 3{ 170 | if cardNumCount == 1{ 171 | return NewSetInfo(LANDLORD_SET_TYPE_THREE,set[0].GetValue(),set[0].GetValue()),nil 172 | }else{ 173 | return nil,errors.New("不是三带牌") 174 | } 175 | }else{ 176 | if cardNumCount == 2 { 177 | 178 | for k,v := range cardNum{ 179 | if v == 3{ 180 | if(pokersNum == 4){ 181 | return NewSetInfo(LANDLORD_SET_TYPE_THREE_PLUS_ONE,k,k),nil 182 | }else{ 183 | return NewSetInfo(LANDLORD_SET_TYPE_THREE_PLUS_TWO,k,k),nil 184 | } 185 | } 186 | } 187 | return nil,errors.New("不是三带牌") 188 | }else{ 189 | return nil,errors.New("不是三带牌") 190 | } 191 | } 192 | } 193 | 194 | //是否是四代一或者四代二 195 | func (self landLordChecker) checkFourPlus(set poker.PokerSet) (*SetInfo,error){ 196 | 197 | pokersNum := set.CountCards() 198 | if pokersNum != 6 && pokersNum != 8{ 199 | return nil,errors.New("不是四带牌") 200 | } 201 | 202 | set.SortAsc() 203 | 204 | cardNum := set.AnalyzeEachCardValueNum() 205 | cardNumCount := len(cardNum) 206 | if cardNumCount == 2{ 207 | k1 := -1 208 | v1 := -1 209 | k2 := -1 210 | v2 := -1 211 | i := 1 212 | for k,v := range cardNum{ 213 | if i == 1{ 214 | k1 = k 215 | v1 = v 216 | }else{ 217 | k2 = k 218 | v2 = v 219 | } 220 | i++ 221 | } 222 | 223 | if pokersNum == 6{ 224 | //支持444455这种四带二 225 | if v1 == 4{ 226 | return NewSetInfo(LANDLORD_SET_TYPE_FOUR_PLUS_TWO,k1,k1),nil 227 | }else if v2 == 4{ 228 | return NewSetInfo(LANDLORD_SET_TYPE_FOUR_PLUS_TWO,k2,k2),nil 229 | }else{ 230 | return nil,errors.New("不是四带牌") 231 | } 232 | }else{ 233 | //不支持44445555这种连续四带四的牌,因为没法判断是四带四还是多连四 234 | //支持44446666这种牌型,以大的作为主牌,即四个6带两对4 235 | if v1 == 4 && k1 != k2+1 && k2 != k1+1{ 236 | k := -1 237 | if k1 > k2 { 238 | k = k1 239 | }else{ 240 | k = k2 241 | } 242 | return NewSetInfo(LANDLORD_SET_TYPE_FOUR_PLUS_FOUR,k,k),nil 243 | }else{ 244 | return nil,errors.New("不是四带牌") 245 | } 246 | } 247 | }else if cardNumCount == 3{ 248 | mainValue := -1 249 | for k,v := range cardNum{ 250 | if v == 4 { 251 | mainValue = k 252 | } 253 | if pokersNum == 6{ 254 | if v == 2 || v == 3 || v == 5 || v == 6{ 255 | return nil,errors.New("不是四带牌") 256 | } 257 | }else{ 258 | if v == 1 || v == 3 || v == 5 || v == 6 { 259 | return nil,errors.New("不是四带牌") 260 | } 261 | } 262 | } 263 | if mainValue == -1{ 264 | return nil,errors.New("不是四带牌") 265 | }else{ 266 | if pokersNum == 6 { 267 | //四带二444456这种 268 | return NewSetInfo(LANDLORD_SET_TYPE_FOUR_PLUS_TWO,mainValue,mainValue),nil 269 | }else{ 270 | //四带四44445566这种 271 | return NewSetInfo(LANDLORD_SET_TYPE_FOUR_PLUS_FOUR,mainValue,mainValue),nil 272 | } 273 | } 274 | } else{ 275 | return nil,errors.New("不是四带牌") 276 | } 277 | } 278 | 279 | //是否多个三带一,或三代二,或不带 280 | func (self landLordChecker) checkMultiThreePlus(set poker.PokerSet) (*SetInfo,error){ 281 | pokerNum := set.CountCards() 282 | if pokerNum < 6 { 283 | return nil,errors.New("不是三顺") 284 | } 285 | 286 | set.SortAsc() 287 | cardNum := set.AnalyzeEachCardValueNum() 288 | 289 | //mainCardValue := -1 //暂存主牌的value,用于比较是否连续 290 | //mainCardNum := 0 //主牌的数量 291 | mainCardValues := []int{} //存放主牌的值 292 | attachCardNum := 0 //附牌的数量 293 | attachCardNumMap := make(map[int]int) //附牌的value和num的map 294 | 295 | for k,v := range cardNum{ 296 | if v == 3{ 297 | mainCardValues = append(mainCardValues,k) 298 | }else{ 299 | attachCardNumMap[k] = v 300 | attachCardNum += v 301 | } 302 | } 303 | BubbleSortIntMin2Max(mainCardValues) 304 | //只包含连续的主牌的数量,不连续的同数量的当做附牌对待 305 | realMainCardValues := []int{} 306 | //主牌连续,且只有一个连续的,其他的间断连续作为附牌处理 307 | for i,value := range mainCardValues{ 308 | if i < len(mainCardValues)-1 && mainCardValues[i] + 1 == mainCardValues[i+1]{ 309 | if len(realMainCardValues) > 0 && value == realMainCardValues[len(realMainCardValues)-1]+1{ 310 | realMainCardValues = append(realMainCardValues,value) 311 | }else if len(realMainCardValues) == 0{ 312 | realMainCardValues = append(realMainCardValues,value) 313 | }else{ 314 | attachCardNumMap[value] = 3 315 | attachCardNum += 3 316 | } 317 | }else if i > 0 && mainCardValues[i] == mainCardValues[i-1]+1{ 318 | if len(realMainCardValues) > 0 && value == realMainCardValues[len(realMainCardValues)-1]+1{ 319 | realMainCardValues = append(realMainCardValues,value) 320 | }else if len(realMainCardValues) == 0{ 321 | realMainCardValues = append(realMainCardValues,value) 322 | }else{ 323 | attachCardNumMap[value] = 3 324 | attachCardNum += 3 325 | } 326 | }else{//该值的牌作为附牌对待 327 | attachCardNumMap[value] = 3 328 | attachCardNum += 3 329 | } 330 | } 331 | 332 | mainCardNum := len(realMainCardValues) 333 | if mainCardNum < 2 {//未构成连续牌型 334 | return nil,errors.New("不是三顺") 335 | } 336 | 337 | //2和王不能参与连顺 338 | if mainCardNum > 1 && realMainCardValues[len(realMainCardValues)-1] > poker.CARD_VALUE_ACE{ 339 | return nil,errors.New("不是三顺") 340 | } 341 | 342 | //没有附牌 343 | if attachCardNum == 0{ 344 | return NewSetInfo(LANDLORD_SET_TYPE_MULITY_THREE,realMainCardValues[0],realMainCardValues[len(realMainCardValues)-1]),nil 345 | }else if mainCardNum == attachCardNum{//三带一 346 | return NewSetInfo(LANDLORD_SET_TYPE_MULITY_THREE_PLUS_ONE,realMainCardValues[0],realMainCardValues[len(realMainCardValues)-1]),nil 347 | }else if mainCardNum*2 == attachCardNum{//三带二 348 | for _,v := range attachCardNumMap{ 349 | if v != 2 && v != 4{ 350 | return nil,errors.New("不是三顺") 351 | } 352 | } 353 | return NewSetInfo(LANDLORD_SET_TYPE_MULITY_THREE_PLUS_TWO,realMainCardValues[0],realMainCardValues[len(realMainCardValues)-1]),nil 354 | }else{ 355 | return nil,errors.New("不是三顺") 356 | } 357 | } 358 | 359 | //是否多个四带一或四代二,或不带 360 | func (self landLordChecker) checkMultiFourPlus(set poker.PokerSet) (*SetInfo,error){ 361 | 362 | pokerNum := set.CountCards() 363 | if pokerNum < 8 || pokerNum%2 != 0 { 364 | return nil,errors.New("不是四顺") 365 | } 366 | 367 | set.SortAsc() 368 | cardNum := set.AnalyzeEachCardValueNum() 369 | 370 | //mainCardValue := -1 //暂存主牌的value,用于比较是否连续 371 | //mainCardNum := 0 //主牌的数量 372 | mainCardValues := []int{} //存放主牌的值 373 | attachCardNum := 0 //附牌的数量 374 | attachCardNumMap := make(map[int]int) //附牌的value和num的map 375 | 376 | for k,v := range cardNum{ 377 | if v == 4{ 378 | mainCardValues = append(mainCardValues,k) 379 | }else{ 380 | attachCardNumMap[k] = v 381 | attachCardNum += v 382 | } 383 | } 384 | BubbleSortIntMin2Max(mainCardValues) 385 | 386 | //只包含连续的主牌的数量,不连续的同数量的当做附牌对待 387 | realMainCardValues := []int{} 388 | //主牌连续,且只有一个连续的,其他的间断连续作为附牌处理 389 | for i,value := range mainCardValues{ 390 | if i < len(mainCardValues)-1 && mainCardValues[i] + 1 == mainCardValues[i+1]{ 391 | if len(realMainCardValues) > 0 && value == realMainCardValues[len(realMainCardValues)-1]+1{ 392 | realMainCardValues = append(realMainCardValues,value) 393 | }else if len(realMainCardValues) == 0{ 394 | realMainCardValues = append(realMainCardValues,value) 395 | }else{ 396 | attachCardNumMap[value] = 4 397 | attachCardNum += 4 398 | } 399 | }else if i > 0 && mainCardValues[i] == mainCardValues[i-1]+1{ 400 | if len(realMainCardValues) > 0 && value == realMainCardValues[len(realMainCardValues)-1]+1{ 401 | realMainCardValues = append(realMainCardValues,value) 402 | }else if len(realMainCardValues) == 0{ 403 | realMainCardValues = append(realMainCardValues,value) 404 | }else{ 405 | attachCardNumMap[value] = 4 406 | attachCardNum += 4 407 | } 408 | }else{//该值的牌作为附牌对待 409 | attachCardNumMap[value] = 4 410 | attachCardNum += 4 411 | } 412 | } 413 | 414 | mainCardNum := len(realMainCardValues) 415 | for mainCardNum < 2{ 416 | return nil,errors.New("不是四顺") 417 | } 418 | 419 | //2和王不能参与连顺 420 | if mainCardNum > 1 && realMainCardValues[len(realMainCardValues)-1] > poker.CARD_VALUE_ACE{ 421 | return nil,errors.New("不是四顺") 422 | } 423 | 424 | //没有附牌 425 | if attachCardNum == 0{//四不带 426 | return NewSetInfo(LANDLORD_SET_TYPE_MULITY_FOUR,realMainCardValues[0],realMainCardValues[len(realMainCardValues)-1]),nil 427 | }else if mainCardNum*2 == attachCardNum{//四带二 428 | return NewSetInfo(LANDLORD_SET_TYPE_MULITY_FOUR_PLUS_TWO,realMainCardValues[0],realMainCardValues[len(realMainCardValues)-1]),nil 429 | }else if mainCardNum*4 == attachCardNum{//四带四 430 | for _,v := range attachCardNumMap{ 431 | if v != 2 && v != 4{ 432 | return nil,errors.New("不是四顺") 433 | } 434 | } 435 | return NewSetInfo(LANDLORD_SET_TYPE_MULITY_FOUR_PLUS_FOUR,realMainCardValues[0],realMainCardValues[len(realMainCardValues)-1]),nil 436 | }else{ 437 | return nil,errors.New("不是四顺") 438 | } 439 | } -------------------------------------------------------------------------------- /setinfo.go: -------------------------------------------------------------------------------- 1 | package pokergame 2 | 3 | const( 4 | LANDLORD_SET_TYPE_SINGLE = iota //"单牌" 5 | LANDLORD_SET_TYPE_DRAGON //"单顺子(5个及以上)" 6 | 7 | LANDLORD_SET_TYPE_PAIR //"对牌" 8 | LANDLORD_SET_TYPE_MULIT_PAIRS //"双顺子(3个及以上)" 9 | 10 | LANDLORD_SET_TYPE_THREE //三张不带牌 11 | LANDLORD_SET_TYPE_THREE_PLUS_ONE //三带一 12 | LANDLORD_SET_TYPE_THREE_PLUS_TWO //三带二 13 | LANDLORD_SET_TYPE_MULITY_THREE //3顺子不带牌 14 | LANDLORD_SET_TYPE_MULITY_THREE_PLUS_ONE //3顺子带1个牌 15 | LANDLORD_SET_TYPE_MULITY_THREE_PLUS_TWO //3顺子带2个牌 16 | 17 | LANDLORD_SET_TYPE_FOUR_PLUS_TWO //四带2张 18 | LANDLORD_SET_TYPE_FOUR_PLUS_FOUR //四带2对 19 | LANDLORD_SET_TYPE_MULITY_FOUR //四顺子不带牌 20 | LANDLORD_SET_TYPE_MULITY_FOUR_PLUS_TWO //四顺子带2张 21 | LANDLORD_SET_TYPE_MULITY_FOUR_PLUS_FOUR //四顺子带4张 22 | 23 | LANDLORD_SET_TYPE_COMMON_BOMB // "炸弹" 24 | LANDLORD_SET_TYPE_JOKER_BOMB // "王炸" 25 | ) 26 | 27 | func GetPokerSetTypeName(pattern int) string { 28 | switch pattern { 29 | case LANDLORD_SET_TYPE_SINGLE: 30 | return "单牌" 31 | case LANDLORD_SET_TYPE_DRAGON: 32 | return "单顺子" 33 | case LANDLORD_SET_TYPE_PAIR: 34 | return "对牌" 35 | case LANDLORD_SET_TYPE_MULIT_PAIRS: 36 | return "双顺子" 37 | case LANDLORD_SET_TYPE_THREE: 38 | return "三张牌" 39 | case LANDLORD_SET_TYPE_THREE_PLUS_ONE: 40 | return "三带一" 41 | case LANDLORD_SET_TYPE_THREE_PLUS_TWO: 42 | return "三带二" 43 | case LANDLORD_SET_TYPE_MULITY_THREE: 44 | return "三顺子不带牌" 45 | case LANDLORD_SET_TYPE_MULITY_THREE_PLUS_ONE: 46 | return "三顺子带一张" 47 | case LANDLORD_SET_TYPE_MULITY_THREE_PLUS_TWO: 48 | return "三顺子带两张" 49 | case LANDLORD_SET_TYPE_FOUR_PLUS_TWO: 50 | return "四带二" 51 | case LANDLORD_SET_TYPE_FOUR_PLUS_FOUR: 52 | return "四带四" 53 | case LANDLORD_SET_TYPE_MULITY_FOUR: 54 | return "四顺子不带牌" 55 | case LANDLORD_SET_TYPE_MULITY_FOUR_PLUS_TWO: 56 | return "四顺子带两张" 57 | case LANDLORD_SET_TYPE_MULITY_FOUR_PLUS_FOUR: 58 | return "四顺子带四张" 59 | case LANDLORD_SET_TYPE_COMMON_BOMB: 60 | return "炸弹" 61 | case LANDLORD_SET_TYPE_JOKER_BOMB: 62 | return "火箭" 63 | default: 64 | return "未定义的牌型" 65 | } 66 | } 67 | 68 | type SetInfo struct{ 69 | setType int 70 | cardValueMinAndMax map[string]int 71 | } 72 | 73 | func NewSetInfo(setType int,minValue int,maxValue int) *SetInfo { 74 | setInfo := SetInfo{ 75 | setType: setType, 76 | cardValueMinAndMax: make(map[string]int), 77 | } 78 | setInfo.cardValueMinAndMax["min"] = minValue 79 | setInfo.cardValueMinAndMax["max"] = maxValue 80 | return &setInfo 81 | } 82 | 83 | func (setInfo *SetInfo)GetRangeWidth() int{ 84 | return setInfo.cardValueMinAndMax["max"] - setInfo.cardValueMinAndMax["min"]+1 85 | } 86 | func (setInfo *SetInfo)GetSetType() int{ 87 | return setInfo.setType 88 | } 89 | func (setInfo *SetInfo)GetSetTypeName() string{ 90 | return GetPokerSetTypeName(setInfo.setType) 91 | } 92 | func (setInfo *SetInfo)GetMinValue() int{ 93 | return setInfo.cardValueMinAndMax["min"] 94 | } 95 | func (setInfo *SetInfo)GetMaxValue() int{ 96 | return setInfo.cardValueMinAndMax["max"] 97 | } 98 | -------------------------------------------------------------------------------- /sorts.go: -------------------------------------------------------------------------------- 1 | package pokergame 2 | //冒泡排序法对整数排序 3 | func BubbleSortIntMin2Max(ints []int){ 4 | length := len(ints) 5 | for i :=0;i ints[j]{ 8 | ints[i],ints[j] = ints[j],ints[i] 9 | } 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /sorts_test.go: -------------------------------------------------------------------------------- 1 | package pokergame 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestBubbleSortIntMin2Max(t *testing.T) { 8 | ints := []int{1,3,5,4,6,2} 9 | BubbleSortIntMin2Max(ints) 10 | if ints[0] != 1 || 11 | ints[1] != 2 || 12 | ints[2] != 3 || 13 | ints[3] != 4 || 14 | ints[4] != 5 || 15 | ints[5] != 6{ 16 | t.Error("TestBubbleSortIntMin2Max err") 17 | } 18 | 19 | } 20 | --------------------------------------------------------------------------------