游戏规则:
游戏用牌为除大小王以外的一副牌,共计52张。玩家人数为固定4人(初版人数),每人随机发5张牌。

  1. 牌型说明
    牌型 赔率(闲家下注) 说明
    无牛 1倍 五张牌中没有任意三张牌点数之和为10的整数倍,例如:a、8、4、k、7。
    有牛 1~2倍 五张牌中有三张的点数之和为10点的整数倍,并且另外两张牌之和与10进行取余,所得之数即为牛几,例如:2、8、j、6、3,即为牛9。牛一到牛6为1倍,牛七到牛九为2倍。
    牛牛 3倍 五张牌中第一组三张牌和第一组二张牌之和分别为10的整数倍,例如:3、7、k、10、j。
    银牛 4倍 五张牌全由10~k组成且只有一张10,例如:10、j、j、q、k。
    金牛 5倍 五张牌全由j~k组成,例如:j、j、q、q、k。
    炸弹 6倍 五张牌中有4张牌点数相同的牌型,例如:2、2、2、2、k。
    五小 10倍 五张牌的点数加起来小于10,且每张牌点数都小于5,例如:a、3、2、a、2。

  2. 牌型比较
    牌型:五小牛 > 炸弹 > 金牛 > 银牛 > 牛牛 > 有牛 > 无牛。
    单张:K > Q > J > 10 > 9 > 8 > 7 > 6 > 5 > 4 > 3 > 2 > A。
    花色:黑桃 > 红桃 > 梅花 > 方块(不采用)。

同牌型间的比较:
无牛:比最大单张大小。
有牛:牛9 > 牛8 > ~ > 牛2 > 牛1;牛数相同庄家赢(庄吃闲)。
牛牛:比最大单张大小。
银牛(四花):比最大单张大小。
金牛(五花):比最大单张大小。
炸弹:比炸弹牌大小。
五小:庄家赢(庄吃闲)。

  1. 定庄押注
    第一盘随机选择庄家,若游戏过程中没有出现“牛九”及以上牌型则继续由上盘玩家担任庄家,若出现牛九牌型则由获得牛九牌型的玩家下盘担任庄家。若在同一盘中有多名玩家出现牛9牌型则再进行大小比较,由最大牌型的玩家下盘担任庄家。所有的大小比较过程均是庄家和闲家比较,闲家和闲家之间不进行比较。

  2. 规则补充
    特殊:当庄家与闲家牌型相同需要比单张时,系统自动比较两家手中最大的一张牌,谁大谁赢;如果两家最大的牌点数相同,则继续比较两家第二大的牌,同理直到第五张比完(不比花色);如果两家所有牌点数相同,默认庄家赢。

1.协议制定

package gameInfo/*该包为游戏信息协议定制*/const (INIT_GAMEINFO       = iotaC2S_LoginInfo_Proto //1 登录信息S2C_LoginInfo_Proto //2 登录返回信息C2S_PipeiGame_Proto          //3 客户端发送服务器 玩家匹配S2C_PipeiGameBroadCast_Proto //4 服务器返回匹配成功 返回房间信息C2S_Score_Proto            //5押注信息S2C_ShuffleBroadCast_Proto //6服务器广播押注信息并发牌C2S_CombineCardsInfo_Proto //7 组牌信息S2C_Settlement_Proto       //8 结算信息)/*json结构体形式
{GameInfo:LoginInfo_Proto,PlayerName:xxx                协议名字+结构体内容
}*///登录信息
type C2S_LoginInfo struct { //玩家登录信息,暂只传昵称GameInfo   intPlayerName string
}type S2C_LoginInfo struct { //服务器返回玩家信息:ID 昵称 余额GameInfo intSucc     boolPlayer   *PlayerInfo
}//匹配信息
type C2S_PipeiGame struct {GameInfo intID       string
}type S2C_PipeiGameBroadCast struct {GameInfo intSucs     boolRoomInfo *Room //房间信息
}//押注信息
type C2S_Score struct {GameInfo intRoomID   string //房间idID       string //个人idChairID  int    //个人椅子idScore    int    //押注数
}//押注信息广播并发牌
type S2C_ShuffleBroadCast struct {GameInfo    intRoomID      string         //房间号(Room.RoomID)ChairsScore []int          //所有的椅子的score已经被更新了BankerID    string         //庄家id(Room.BankerID)Poker       *PersonalPoker //只有自己的牌
}type C2S_CombineCardsInfo struct { //客户端发送组牌信息GameInfo      intID            stringRoomID        stringChairID       intPokerType     int   //牌型Poker         []int //所有的牌NiuCardsInfo  []int //牛牌组成LastCardsInfo []int //余牌组成
}type S2C_Settlement struct { //服务器发送结算信息GameInfo intSuccs    map[int]bool //胜负标志位RoomInfo *Room
}//服务器内部逻辑结构体
type PlayerInfo struct {ID          stringPlayerName  stringPlayerMoney int
}type PersonalPoker struct {ID            string //存人的IDCards         []intNiuCardsInfo  []intLastCardsInfo []intPokerType     int
} //牌type Chair struct {ChairID  intPlayer   *PlayerInfoLefttime intScore    int //下注底分Poker    *PersonalPoker
}type Room struct {RoomID   stringChairs   [4]*ChairBankerID string //庄家号//roomLimit  int//RoomStatus int
}

2.主函数启用websocket

package mainimport ("flag""fmt""net/http"//"runtime""go-concurrentMap-master""github.com/golang/glog""github.com/gorilla/websocket"
)//ip:ws://100.64.15.187:8800/ws
var (M *concurrent.ConcurrentMap
)func init() {M = concurrent.NewConcurrentMap() //初始化玩家信息MAPflag.Set("alsologtostderr", "true") // 日志写入文件的同时,输出到stderrflag.Set("log_dir", "./log")        // 日志文件保存目录flag.Set("v", "3")                  // 配置V输出的等级。flag.Parse()return
}func httphandle(resp http.ResponseWriter, req *http.Request) {up := websocket.Upgrader{// 检查区域 可以自行设置是POST 或者GET请求 还有URL等信息 这里直接设置表示都接受CheckOrigin: func(r *http.Request) bool {return true},}conn, err := up.Upgrade(resp, req, nil)if err != nil {glog.Error("upgrede failed")return} else {glog.Info("now is websocket")}c := &Client{Socket: conn, //ws连接器}c.ReadFromClient()}func main() {http.HandleFunc("/ws", httphandle)err := http.ListenAndServe(":8800", nil)if err != nil {fmt.Println("ListenAndServe : ", err)return}}

3.协议处理函数

package mainimport ("crypto/md5""encoding/json""fmt""gameInfo""math/rand""reflect" //反射"time"//"go-concurrentMap-master""AI""github.com/golang/glog""github.com/gorilla/websocket"
)//连接存储结构
type Client struct {Socket  *websocket.Conn //ws连接器StrROOM string          //房间IDPlayer  *gameInfo.PlayerInfo
}//json 转化为map(信息反序列化)
func Json2map(content []byte) (s map[string]interface{}, err error) {var result map[string]interface{}if err := json.Unmarshal(content, &result); err != nil {glog.Error("Json2map:", err.Error())//fmt.Println(err)return nil, err}return result, nil
}//json 返回数据(信息序列化)
func (this *Client) MsgSend(senddata interface{}) {err := this.Socket.WriteJSON(senddata) //返回json化的数据if err != nil {glog.Error(err)return}return
}func (this *Client) ReadFromClient() {for {var content []bytevar err errorif _, content, err = this.Socket.ReadMessage(); err != nil {break}if len(content) == 0 {break}////glog.Info(content)//正常处理数据//并发go this.SyncMeassgeFun(content)}return
}func (this *Client) SyncMeassgeFun(content []byte) {defer glog.Flush()if GameMsg, err := Json2map(content); err == nil {//处理函数this.HandleFunc(GameMsg["GameInfo"], GameMsg)} else {glog.Error("解析失败:", err.Error())//fmt.Println("解析失败")}
}//协议判断函数
func (this *Client) HandleFunc(MsgType interface{}, GameMsg map[string]interface{}) {switch MsgType {case float64(gameInfo.C2S_LoginInfo_Proto):{this.LoginInfo_Handler(GameMsg)}case float64(gameInfo.C2S_PipeiGame_Proto):{this.Pipei_Handler(GameMsg)}case float64(gameInfo.C2S_Score_Proto):{this.Score_Handler(GameMsg)}case float64(gameInfo.C2S_CombineCardsInfo_Proto):{this.CombineCardsInfo_Handler(GameMsg)}default:panic("子协议:不存在!")}}//协议处理函数/**登录信息处理**/
/*测试json
{
"GameInfo": 1,
"PlayerName": "rubylee"
}
*//*生成id函数*/
//生成随机字符串
func RandString(len int) string {r := rand.New(rand.NewSource(time.Now().UnixNano()))bytes := make([]byte, len)for i := 0; i < len; i++ {b := r.Intn(26) + 65bytes[i] = byte(b)}return string(bytes)
}//md5转换函数
func md5Change(str string) string {md5String := fmt.Sprintf("%x", md5.Sum([]byte(str)))return md5String
}func (this *Client) LoginInfo_Handler(GameMsg map[string]interface{}) {defer glog.Flush() //刷新日志流if GameMsg["PlayerName"] == nil {glog.Error("协议LoginInfo_Proto ,登录功能错误:登录名不存在!")this.Socket.Close() //关闭连接return}this.Player = &gameInfo.PlayerInfo{ID:          md5Change(RandString(6)),       //ID获取函数,PlayerName:  GameMsg["PlayerName"].(string), //断言PlayerMoney: 10000,                          //默认金钱}data := &gameInfo.S2C_LoginInfo{GameInfo: gameInfo.S2C_LoginInfo_Proto,Player:   this.Player,Succ:     true,}//数据保存M.Put(this.Player.ID, this)//发送数据给客户端this.MsgSend(data)return
}/**匹配信息处理**/
func (this *Client) Pipei_Handler(GameMsg map[string]interface{}) {//匹配逻辑//1.队列形式defer glog.Flush()strID := GameMsg["ID"].(string)if strID != this.Player.ID {glog.Error("匹配错误:id值不一致!")return}val, err := M.Get(this.Player.ID)if err != nil {glog.Error("匹配错误:不在全局玩家数据表中!")return}glog.Info("匹配开始!")PutMatchList(val.(*Client).Player)return
}/**积分信息处理**/
func (this *Client) Score_Handler(GameMsg map[string]interface{}) {defer glog.Flush()roomID := GameMsg["RoomID"].(string)chairID := int(GameMsg["ChairID"].(float64))pID := GameMsg["ID"].(string)new_score := int(GameMsg["Score"].(float64))GRoomManagerPtr.RoomLock.RLock()           //加锁_, ok := GRoomManagerPtr.GRoomData[roomID] //取房间//核对有无房间if !ok {GRoomManagerPtr.RoomLock.RUnlock() //解锁glog.Error("Score_Handler:房间信息不存在!")return}//核对个人idif pID != GRoomManagerPtr.GRoomData[roomID].Chairs[chairID].Player.ID {GRoomManagerPtr.RoomLock.RUnlock() //解锁glog.Errorf("Score_Handler:房间号%v,座位号%v,的ID信息不一致!", roomID, chairID)glog.Errorln("pid", pID, "new_score", new_score)return} else {//修改底分信息GRoomManagerPtr.GRoomData[roomID].Chairs[chairID].Score = new_score}GRoomManagerPtr.RoomLock.RUnlock()return
}func interArray2intArray(q []interface{}) []int {q1 := make([]int, 0)for i := 0; i < len(q); i++ {q1 = append(q1, int(q[i].(float64)))}return q1
}/**组牌信息处理**/
func (this *Client) CombineCardsInfo_Handler(GameMsg map[string]interface{}) {defer glog.Flush()roomID := GameMsg["RoomID"].(string)                                           //房间号chairID := int(GameMsg["ChairID"].(float64))                                   //椅子号pID := GameMsg["ID"].(string)                                                  //用户IDpoker := interArray2intArray(GameMsg["Poker"].([]interface{}))                 //牌型new_NiuCards := interArray2intArray(GameMsg["NiuCardsInfo"].([]interface{}))   //新的牛牌new_LastCards := interArray2intArray(GameMsg["LastCardsInfo"].([]interface{})) //新的余牌new_PokerType := int(GameMsg["PokerType"].(float64))                           //新的牌型编号GRoomManagerPtr.RoomLock.RLock()           //加锁_, ok := GRoomManagerPtr.GRoomData[roomID] //取房间//核对有无房间if !ok {GRoomManagerPtr.RoomLock.RUnlock() //解锁glog.Error("CombineCardsInfo_Handler:房间信息不存在!")return}//核对个人idif pID != GRoomManagerPtr.GRoomData[roomID].Chairs[chairID].Player.ID {GRoomManagerPtr.RoomLock.RUnlock() //解锁glog.Errorf("CombineCardsInfo_Handler:房间号%v,座位号%v,的ID信息不一致!", roomID, chairID)return} else if !reflect.DeepEqual(poker, GRoomManagerPtr.GRoomData[roomID].Chairs[chairID].Poker.Cards) {//牌不一致GRoomManagerPtr.RoomLock.RUnlock() //解锁glog.Errorf("CombineCardsInfo_Handler:房间号%v,座位号%v,的牌信息不一致!", roomID, chairID)return} else {//牌型校验cardstmp := &gameInfo.PersonalPoker{Cards:         poker,NiuCardsInfo:  new_NiuCards,LastCardsInfo: new_LastCards,PokerType:     new_PokerType,}if !AI.CheckType(cardstmp) {GRoomManagerPtr.RoomLock.RUnlock() //解锁glog.Errorf("CombineCardsInfo_Handler:房间号%v,座位号%v,的牌型校验失败!", roomID, chairID)return}GRoomManagerPtr.GRoomData[roomID].Chairs[chairID].Poker.NiuCardsInfo = new_NiuCardsGRoomManagerPtr.GRoomData[roomID].Chairs[chairID].Poker.LastCardsInfo = new_LastCardsGRoomManagerPtr.GRoomData[roomID].Chairs[chairID].Poker.PokerType = new_PokerType}GRoomManagerPtr.RoomLock.RUnlock()return
}

4.匹配函数

package mainimport ("gameInfo""time""github.com/golang/glog"
)var (GMatchChan chan map[string]*gameInfo.PlayerInfoGmap       map[string]*gameInfo.PlayerInfo
)func init() {GMatchChan = make(chan map[string]*gameInfo.PlayerInfo, 1000)Gmap = make(map[string]*gameInfo.PlayerInfo)go Timer()
}func PutMatchList(data *gameInfo.PlayerInfo) {Gmap[data.ID] = data
}//匹配timer
func Timer() {for {select {case <-time.After(time.Millisecond * 1):{// 进行数据验证 -- 确保我们链接信息完全获取到// 不够严谨,如果不验证玩家数据是否保存成功,会导致客户端一个匹配成功,无法游戏。// 确保4个人, 如果满足len(Gmap)%4 == 0  --->  GMatchChanif len(Gmap)%4 == 0 && len(Gmap) != 0 {datatmp := make(map[string]*gameInfo.PlayerInfo)for k, v := range Gmap {if _, err := M.Get(k); err != nil {glog.Error("玩家未在在线玩家数据表中!!")delete(Gmap, k)break}datatmp[k] = vdelete(Gmap, k)}SendGMatchChan(datatmp)}}}}
}func SendGMatchChan(data map[string]*gameInfo.PlayerInfo) {if len(data) == 0 {return}GMatchChan <- data
}

5.游戏逻辑代码

package mainimport ("AI""gameInfo""math/rand""strconv""sync""time""github.com/golang/glog"
)/*
房间结构
房间管理器 manager1.玩家数据2.房间的销毁3.游戏数据存储type Room struct {RoomID intChair  [4]*ChairBanker *Chair //庄家号//roomLimit  intRoomStatus int
}
*/
var (GRoomManagerPtr *GRoomSTData
)type GRoomSTData struct {GRoomData map[string]*gameInfo.RoomRoomLock  *sync.RWMutex //并发安全
}func init() {GRoomManagerPtr = &GRoomSTData{GRoomData: make(map[string]*gameInfo.Room), //每一个匹配成功数据总和,例如:A,B,C,DRoomLock:  new(sync.RWMutex),}go matchtimer()
}//获取全局RoomID
func GetRoomID() int {GRoomManagerPtr.RoomLock.RLock()ilen := len(GRoomManagerPtr.GRoomData) + 1000GRoomManagerPtr.RoomLock.RUnlock()return ilen
}/*
说明:1. 4人组合一个房间2. 发送广播消息3.  房间生成规则4. 处理数据结构线程通信;
*/
func matchtimer() {startTimer := time.NewTicker(time.Millisecond * 10)for {select {case <-startTimer.C:{datachan := <-GMatchChan //拿绑定的4人strRoomID := strconv.Itoa(GetRoomID())GRoomManagerPtr.RoomLock.RLock()roomtmp := GRoomManagerPtr.GRoomData[strRoomID] //取房间GRoomManagerPtr.RoomLock.RUnlock()glog.Infoln("房间号", strRoomID)if roomtmp == nil {roomtmp = &gameInfo.Room{}}roomtmp.RoomID = strRoomIDdealgamelogic(roomtmp, datachan, strRoomID)}}}
}
func dealgamelogic(roomtmp *gameInfo.Room, datachan map[string]*gameInfo.PlayerInfo, strRoomID string) {//确定庄家b := rand.Intn(4) //0-3号index := 0for id, _ := range datachan {val, _ := M.Get(id)/*type Chair struct {ChairID  intPlayer   PlayerInfoLefttime int}*/chair := &gameInfo.Chair{ChairID:  index,Player:   val.(*Client).Player,Lefttime: 5,Score:    10,}/*type Room struct {RoomID stringChairs [4]*ChairBanker string //庄家号//roomLimit  int//RoomStatus int}*/roomtmp.Chairs[index] = chairval.(*Client).StrROOM = strRoomIDindex++}roomtmp.BankerID = roomtmp.Chairs[b].Player.ID //保存庄家号//再次遍历发送消息index = 0for id, _ := range datachan {val, _ := M.Get(id)/* type S2C_PipeiGameBroadCast struct {GameInfo intSucs     boolRoomInfo Room      //房间信息}*/data := gameInfo.S2C_PipeiGameBroadCast{GameInfo: gameInfo.S2C_PipeiGameBroadCast_Proto,Sucs:     true,RoomInfo: roomtmp,}//广播 消息val.(*Client).MsgSend(data)index++}//房间存储全局房间管理器GRoomManagerPtr.GRoomData[strRoomID] = roomtmp/*************等待玩家给出底分***********///定时器5s  等玩家给出底分timer := time.NewTimer(7 * time.Second) //等5S<-timer.C/***************开始发牌**********/glog.Info("发牌开始!")q := AI.Shuffle(4)ScoreSet := make([]int, 4) //存储新的底分for i := 0; i < len(roomtmp.Chairs); i++ {pokertmp := &gameInfo.PersonalPoker{}pokertmp.ID = roomtmp.Chairs[i].Player.ID//该语句需要在下一条之前 因为MAXtype函数中 会对q[i]按照牛牛牌的逻辑从大到小排序 使得发送到客户端的牌是 从左到右依次减小的pokertmp.NiuCardsInfo, pokertmp.LastCardsInfo, pokertmp.PokerType = AI.MaxType(q[i]) //预设为最大牌型pokertmp.Cards = q[i]roomtmp.Chairs[i].Poker = pokertmpScoreSet[i] = roomtmp.Chairs[i].Score //存储每张chair的新底分}//广播发牌信息index = 0for id, _ := range datachan {val, _ := M.Get(id)roomtmp.Chairs[index].Poker.ID = id/*type S2C_ShuffleBroadCast struct {GameInfo intRoomID   string        //房间号(Room.RoomID)Chairs   [4]*Chair     //所有的椅子 (Room.Chairs)里面的score已经被更新了BankerID string        //庄家id(Room.BankerID)Poker    PersonalPoker //只有自己的牌}*/data := gameInfo.S2C_ShuffleBroadCast{GameInfo:    gameInfo.S2C_ShuffleBroadCast_Proto,RoomID:      strRoomID,ChairsScore: ScoreSet, //返回新的底分Poker:       roomtmp.Chairs[index].Poker,}//广播 消息val.(*Client).MsgSend(data)index++}/**********等待玩家组牌信息*************///定时器20s  等待玩家给出牌型timer = time.NewTimer(5 * time.Second) //等10S<-timer.C/***************开始结算*************/glog.Info("结算开始!")/*type S2C_Settlement struct { //服务器发送结算信息GameInfo intsuccs    map[int]bool //胜负标志位RoomInfo *Room}*/dataSettlment := &gameInfo.S2C_Settlement{GameInfo: gameInfo.S2C_Settlement_Proto,Succs:    make(map[int]bool),}banker_poker := roomtmp.Chairs[b].Poker //庄家的牌型for index := 0; index < len(roomtmp.Chairs); index++ {if index == b {continue} //跳过庄家自身//牌型比较flag, multiple := AI.Settlement(banker_poker, roomtmp.Chairs[index].Poker)//庄家赢if flag {tmpscore := roomtmp.Chairs[index].Player.PlayerMoney - multiple*roomtmp.Chairs[index].Scoreif tmpscore < 0 {roomtmp.Chairs[index].Player.PlayerMoney = 0} else {roomtmp.Chairs[index].Player.PlayerMoney = tmpscore}dataSettlment.Succs[index] = falseroomtmp.Chairs[b].Player.PlayerMoney += multiple * roomtmp.Chairs[index].Score //庄家赢钱} else {//闲家赢tmpscore := roomtmp.Chairs[b].Player.PlayerMoney - multiple*roomtmp.Chairs[index].Scoreif tmpscore < 0 {roomtmp.Chairs[b].Player.PlayerMoney = 0} else {roomtmp.Chairs[b].Player.PlayerMoney = tmpscore}dataSettlment.Succs[index] = trueroomtmp.Chairs[index].Player.PlayerMoney += multiple * roomtmp.Chairs[index].Score //闲家赢钱}}//保存全局房间信息dataSettlment.RoomInfo = roomtmp//广播结算信息for id, _ := range datachan {val, _ := M.Get(id)val.(*Client).MsgSend(dataSettlment)}/*********游戏结束********/glog.Info("游戏结束!")//释放房间信息GRoomManagerPtr.RoomLock.RLock()delete(GRoomManagerPtr.GRoomData, strRoomID)GRoomManagerPtr.RoomLock.RUnlock()
}

6.游戏算法
(1).牌型检查算法

package AI/*
黑桃:A 2 3 4 5 6 7 8 9 10 J  Q  K1                 10 11 12 13
红桃:                  23 24 25 26
方块:                  36 37 38 39
梅花:                  49 50 51 52
*/
import ("gameInfo""github.com/golang/glog"
)/*const pokerType{niu=0//无牛noniu=1//有牛bomb=2//炸弹fives=3//五小
}*/
//type PlayerInfo struct {//  ID          string
//  playerName  string
//  playerMoney int
//}
//type CombineCardsInfo struct {//  user          PlayerInfo
//  pokerType     int
//  niuCardsInfo  []int
//  lastCardsInfo []int
//}//判断是否是炸弹
func checkIsbomb(c *gameInfo.PersonalPoker) bool {t := c.NiuCardsInfo[0]for i := 1; i < len(c.NiuCardsInfo); i++ {if t != c.NiuCardsInfo[i] {glog.Error("Bomb:PokerTyper Error!")return false}}glog.Info("Bomb:Checked passed!")return true
}//判断是否是五小
func checkIsfives(c *gameInfo.PersonalPoker) bool {sum, n := 0, 0for _, v := range c.NiuCardsInfo {n = v % 13if n >= 10 || n == 0 {sum += 10} else {sum += n}}if sum > 10 && c.PokerType == 0 {glog.Info("noNiu:check passed!")return true}if sum <= 10 && c.PokerType == 14 {glog.Info("fivesmall:check passed!")return true}glog.Error("fivesmall or noNiu:PokerTyper Error!")return false}//判断是否是牛
func checkIsniu(c *gameInfo.PersonalPoker) bool {sum1, sum2, n := 0, 0, 0isGniu := true //金牛isSniu := true //银牛for _, v := range c.NiuCardsInfo {n = v % 13if n < 10 && n > 0 {sum1 += n} else {sum1 += 10}if _, ok := sniuSet[v]; !ok {isSniu = falseisGniu = false}if _, ok := gniuSet[v]; !ok && isSniu {isGniu = false}}for _, v := range c.LastCardsInfo {n = v % 13if n < 10 && n > 0 {sum2 += n} else {sum2 += 10}if _, ok := sniuSet[v]; !ok {isSniu = falseisGniu = false}if _, ok := gniuSet[v]; !ok && isSniu {isGniu = false}}if isGniu && c.PokerType == 12 {glog.Info("gniu:check passed!")return true}if isSniu && c.PokerType == 11 {glog.Info("sniu:check passed!")return true}if sum1%10 == 0 && c.PokerType == sum2%10 {glog.Info("niu %v:check passed!", c.PokerType)return true}if sum1%10 == 0 && c.PokerType == 10 && 0 == sum2%10 {glog.Info("niuniu:check passed!")return true}glog.Error("ISNiu:PokerTyper Error!")return false
}func CheckType(c *gameInfo.PersonalPoker) bool {defer glog.Flush()switch c.PokerType {case 0, 14: // 无牛 和 五小return checkIsfives(c)case 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12: //牛1到牛9 + 牛牛 +银牛 +金牛return checkIsniu(c)case 13: //炸弹return checkIsbomb(c)default:glog.Error("%v:pokerType is illegal!", c.ID)}return false
}

(2)最大牌型算法

package AIimport ("gameInfo""sort""github.com/golang/glog"
)/*
黑桃:A 2 3 4 5 6 7 8 9 10 J  Q  K1 2                10 11 12 13
红桃:  15                23 24 25 26
方块:  28               36 37 38 39
梅花:  41               49 50 51 52
*/
//const (             倍数
//  noniu  = 0 //无牛  1倍
//  niu1   = 1 //牛一  1
//  niu2   = 2        1
//  niu3   = 3        1
//  niu4   = 4        1
//  niu5   = 5        1
//  niu6   = 6        1
//  niu7   = 7        2
//  niu8   = 8
//  niu9   = 9
//  niuniu = 10 //牛牛   3
//  sniu   = 11 //银牛   4
//  gniu   = 12 //金牛   5
//  bomb   = 13 //炸弹   6
//  fives  = 14 //五小   10
//)//倍数计算函数
func calmultiple(pokertype int) int {//1倍if pokertype >= 0 && pokertype <= 6 {return 1}//2倍if pokertype >= 7 && pokertype <= 9 {return 2}//3-6倍if pokertype >= 10 && pokertype <= 13 {return pokertype - 7}//10倍if pokertype == 14 {return 10}//牌型错误glog.Error("Pokertype Error!")glog.Flush()return -1}//比较计算函数
func Settlement(banker *gameInfo.PersonalPoker, player *gameInfo.PersonalPoker) (bool, int) {if banker.PokerType > player.PokerType {/*庄家大于闲家*/return true, calmultiple(banker.PokerType)} else if banker.PokerType < player.PokerType {/*闲家大于庄家*/return false, calmultiple(player.PokerType)} else {/*牌型一致时*/if (banker.PokerType >= 1 && banker.PokerType <= 9) || banker.PokerType == 14 {return true, calmultiple(banker.PokerType)} //牛数相同或者五小 庄家赢if banker.PokerType == 13 {if compare(banker.NiuCardsInfo, player.NiuCardsInfo) {return true, calmultiple(banker.PokerType)} else {return false, calmultiple(player.PokerType)}} //同为炸弹 比较炸弹牌if compare(banker.Cards, player.Cards) {return true, calmultiple(banker.PokerType)} else {return false, calmultiple(player.PokerType)} //其他 比较最大单张大小}}//相同牌型比较函数
func compare(banker []int, player []int) bool {for i := 0; i < len(banker); i++ {temp1 := banker[i] % 13temp2 := player[i] % 13if temp1 == 0 {temp1 = 13}if temp2 == 0 {temp2 = 13}if temp1 > temp2 {return true} else if temp1 < temp2 {return false}}return true
}//最优牌型函数
var (gniuSet = map[int]bool{11: true, 12: true, 13: true, 24: true, 25: true, 26: true,37: true, 38: true, 39: true, 50: true, 51: true, 52: true,}sniuSet = map[int]bool{11: true, 12: true, 13: true, 24: true, 25: true, 26: true,37: true, 38: true, 39: true, 50: true, 51: true, 52: true,10: true, 23: true, 36: true, 49: true,}
)func judgeNiu(pokerTen []int, poker []int, sum int) ([]int, []int, int) {maxNiu := 0 //无牛niuCardsInfo := make([]int, 3)lastCardsInfo := make([]int, 0)//转化为找pokerTen的三数之和为10n := len(pokerTen)for i := 0; i < n; i++ {for j := i + 1; j < n; j++ {for k := j + 1; k < n; k++ {niuSum := pokerTen[i] + pokerTen[j] + pokerTen[k]if niuSum%10 == 0 {tempNiu := (sum - niuSum) % 10if tempNiu == 0 {tempNiu = 10} //牛牛//判断当前牛是否最大if tempNiu > maxNiu {niuCardsInfo[0] = poker[i]niuCardsInfo[1] = poker[j]niuCardsInfo[2] = poker[k]lastCardsInfo = []int{}lastCardsInfo = append(lastCardsInfo, poker[:i]...)lastCardsInfo = append(lastCardsInfo, poker[i+1:j]...)lastCardsInfo = append(lastCardsInfo, poker[j+1:k]...)lastCardsInfo = append(lastCardsInfo, poker[k+1:]...)maxNiu = tempNiu}/*else if tempNiu == maxNiu { //当前牛和最大牛相同tNiuCardsInfo := make([]int, 3)tNiuCardsInfo[0] = poker[i]tNiuCardsInfo[1] = poker[j]tNiuCardsInfo[2] = poker[k]tlastCardsInfo := []int{}tlastCardsInfo = append(tlastCardsInfo, poker[:i]...)tlastCardsInfo = append(tlastCardsInfo, poker[i+1:j]...)tlastCardsInfo = append(tlastCardsInfo, poker[j+1:k]...)tlastCardsInfo = append(tlastCardsInfo, poker[k+1:]...)niuCardsInfo, lastCardsInfo = compare(tNiuCardsInfo, tlastCardsInfo, niuCardsInfo, lastCardsInfo)}*/}}}}if maxNiu == 0 {return poker, lastCardsInfo, 0}return niuCardsInfo, lastCardsInfo, maxNiu
}func MaxType(poker []int) ([]int, []int, int) {//排序函数sort.Slice(poker, func(i, j int) bool {i1, j1 := poker[i]%13, poker[j]%13if i1 == 0 {return true} else if j1 == 0 {return false} else {return i1 > j1}})pokerTimes := make(map[int]int) //出现次数统计pokerTen := make([]int, 5)      //对10取余sum := 0                        //统计总数isGniu := true                  //金牛判断位isSniu := true                  //银牛判断位for i := 0; i < len(poker); i++ {if poker[i]%13 > 0 && poker[i]%13 < 10 {sum += poker[i] % 13pokerTen[i] = poker[i] % 13pokerTimes[poker[i]%13]++} else {sum += 10pokerTen[i] = 10if poker[i]%13 == 0 {pokerTimes[13]++} else {pokerTimes[poker[i]%13]++}}if _, ok := sniuSet[poker[i]]; !ok {isSniu = falseisGniu = false}if _, ok := gniuSet[poker[i]]; !ok && isSniu {isGniu = false}}niuCardsInfo := make([]int, 0)lastCardsInfo := make([]int, 0)//返回五小if sum <= 10 {niuCardsInfo = pokerreturn niuCardsInfo, lastCardsInfo, 14}//返回炸弹for k, v := range pokerTimes {if v == 4 {real := 0if poker[0]%13 == 0 {real = 13} else {real = poker[0] % 13}if k == real {niuCardsInfo = poker[0:4]lastCardsInfo = poker[4:]} else {niuCardsInfo = poker[1:]lastCardsInfo = poker[0:1]}return niuCardsInfo, lastCardsInfo, 13}}//返回金牛if isGniu {niuCardsInfo = poker[0:3]lastCardsInfo = poker[3:]return niuCardsInfo, lastCardsInfo, 12}//返回银牛if isSniu {niuCardsInfo = poker[0:3]lastCardsInfo = poker[3:]return niuCardsInfo, lastCardsInfo, 11}return judgeNiu(pokerTen, poker, sum)//判断几牛
}

(3)洗牌算法

package AIimport ("math/rand""time"
)func calculate(arr []int) {rand.Seed(time.Now().Unix())for i := len(arr) - 1; i >= 0; i-- {/* 初始化随机数发生器 */j := rand.Int() % (i + 1)arr[i], arr[j] = arr[j], arr[i]}
}
func Shuffle(playerNum int) [][]int {pokerSet := make([]int, 52)playerPokerSets := make([][]int, playerNum)for i := 0; i < playerNum; i++ {playerPokerSets[i] = make([]int, 5)}for i := 0; i < 52; i++ {pokerSet[i] = i + 1}//洗牌calculate(pokerSet)//fmt.Println(pokerSet)//发牌index := 0for i := 0; i < playerNum; i++ {for j := 0; j < 5; j++ {playerPokerSets[i][j] = pokerSet[index]index++}}return playerPokerSets
}

客户端采用了cocos creator实现,代码就下次更吧!

基于go语言的牛牛游戏服务器搭建相关推荐

  1. h5网页服务器,h5网页游戏服务器搭建

    h5网页游戏服务器搭建 内容精选 换一换 本文介绍了云手机CPH产品新特性和对应的文档动态,新特性将在各个区域(Region)陆续发布,欢迎体验. 本章节通过示例项目"小蝌蚪即时交互游戏&q ...

  2. linux环境下企业基于域名访问的web于电子邮件服务器 论文,基于Linux平台的企业邮件服务器搭建...

    我失骄杨君失柳,杨柳轻飏直上重霄九.得道多助,失道寡助.身后有余忘缩手,眼前无路想回头.鸟宿池边树,僧敲月下门.想当年,金戈铁马,气吞万里如虎. 本文由418133804贡献 pdf文档可能在WAP端 ...

  3. c语言五子棋学年论文,基于c语言五子棋小游戏生本科论文.doc

    基于c语言五子棋小游戏生本科论文 五子棋小游戏 需求分析 现在有越来越多的人使用电脑,而且五子棋的受众广泛但实体棋操作较为繁琐且平时较难实现,所以电脑版的五子棋游戏应运而生.大家对于这个小游戏的需求如 ...

  4. 基于Ubuntu18.04下深度学习服务器搭建

    基于Ubuntu18.04下深度学习服务器搭建 目录: 基于Ubuntu18.04下深度学习服务器搭建 主要模块组成 Anaconda安装 CUDA安装 pytorch安装 CuDNN安装 其他常用指 ...

  5. 基于c语言的小游戏,--基于C语言的小游戏设计.doc

    --基于C语言的小游戏设计.doc 级丌 密公 本科生毕业(学位)论文 基于c语言的爪游软设计 李俊佶 (2009061322) TOC \o "1-5" \h \z 指导教师姓名 ...

  6. 我的世界java版GTA服务器_我的世界Java版游戏服务器搭建

    本文简要介绍在Linux云服务器上部署我的世界Java版服务端的方法 一.选购云服务器 建议使用CentOS7.x或8.x系统. 二.远程登录服务器 可以使用云服务商提供的网页登录的方式,也可以SSH ...

  7. 项目目录游戏服务器,Yivgame是一个基于go-kit的微服务游戏服务器

    yivgame Yivgame是用go语言基于go-kit写的一套微服务架构游戏服务器方案,它不是一个框架,是一整套游戏服务器实例,每个模块只保留了一份示例代码实现.除了游戏服务器(长连接),还包含针 ...

  8. 基于Python语言的PUBG游戏数据可视化分析系统

    [success]写于2019年大作业[/success] 博客链接:https://www.iamzlt.com/?p=299 代码链接请到博客链接内查看. 摘要 随着网络技术的兴起和普及,网络游戏 ...

  9. c语言语音控制游戏文献,C语言课程设计-基于C语言推箱子游戏设计-毕业论文文献.doc...

    gd工程职业技术学院毕业论文 基于C语言的推箱子游戏设计 Design of the push box Based on Combined Language 作者姓名: 学科专业: 应用电子技术 学院 ...

  10. 从入门到入土:基于C语言实现并发Web服务器|父进程子进程|代码展示

    此博客仅用于记录个人学习进度,学识浅薄,若有错误观点欢迎评论区指出.欢迎各位前来交流.(部分材料来源网络,若有侵权,立即删除) 本人博客所有文章纯属学习之用,不涉及商业利益.不合适引用,自当删除! 若 ...

最新文章

  1. 如何使用Exchange邮件组仲裁
  2. Potocol Buffer详解
  3. Android的启动脚本–init.rc
  4. python animation 轨迹_Python实例:自动轨迹绘制
  5. 计算机专业核心技术,计算机系多媒体核心技术实验室建设专业方案(10页)-原创力文档...
  6. Active Record 数据库迁移总结
  7. Tips--更改Jupyter Notebook的默认工作路径
  8. 十三不香了?不止去掉刘海,iPhone14或改用QLC闪存:最高2TB容量
  9. astr在python_python学习之初识字符串
  10. linux内核字符驱动设备,Linux学习笔记——linux内核字符设备驱动-Go语言中文社区...
  11. 多个Wyze 摄像头漏洞可导致攻击者接管设备并访问视频
  12. KnockoutJS 3.X API 第四章 表单绑定(11) options绑定
  13. 【图像边缘检测】基于matlab GUI神经网络算法边缘检测(带面板)【含Matlab源码 1346期】
  14. matlab中的多行注释
  15. lofter 爬虫_Python网络爬虫1 - 爬取网易LOFTER图片
  16. 在腾讯,我们如何做 Code Review
  17. 关于自行修改人人商城模板文件目录指引
  18. POI之excel固定模板导出
  19. 计算机的信息表示(进制的转换)
  20. jsp/servlet过滤器和struts2拦截器的有什么区别

热门文章

  1. 一篇文章简单入门SpringCloud服务网关
  2. 图像坐标球面投影_从球面到平面的投影
  3. 计量经济学 联合假设检验 F统计量
  4. Word——如何固定文章中的公式
  5. RobotStudio软件:ABB机器人喷涂虚拟仿真
  6. 给你10个市场数据调研报告的免费下载网站!以后数据从这里找!
  7. 突破固化思维,如何快速做好陌生领域数据分析?
  8. NPOI导出数据到Excel
  9. 我心中接地气的大数据【前言】
  10. Redis重启数据丢失问题