package tienlen import ( "encoding/json" "fmt" "math" "math/rand" "sort" "strings" "time" "mongo.games.com/goserver/core/logger" tienlenApi "mongo.games.com/game/api3th/smart/tienlen" "mongo.games.com/game/common" rule "mongo.games.com/game/gamerule/tienlen" "mongo.games.com/game/gamesrv/base" "mongo.games.com/game/model" "mongo.games.com/game/proto" "mongo.games.com/game/protocol/tienlen" "mongo.games.com/game/srvdata" ) type BilledInfo struct { Round int32 // 第几局 ChangeScore int64 // 积分变化 Score int64 // 结算后积分 } type Item struct { Id int32 Num int64 } // 房间上的额外数据 type TienLenSceneData struct { *base.Scene //场景 players map[int32]*TienLenPlayerData //玩家信息 seats [4]*TienLenPlayerData //玩家 poker *rule.Poker //扑克牌对象 curGamingPlayerNum int //当前在玩玩家数量 lastGamingPlayerNum int //上局参与游戏的人数 lastOpPos int32 //前一个出牌的玩家位置 currOpPos int32 //当前等待出牌的玩家位置 lastBombPos int32 //上一个出炸弹的玩家位置 curBombPos int32 //当前出炸弹的玩家位置 roundScore int32 //小轮分 startOpPos int32 //开始操作的玩家,拿最小牌的先出牌 curMinCard int32 //当局最小牌值 tianHuSnids []int32 //天胡玩家id winSnids []int32 //赢家id lastWinSnid int32 //上局赢家id masterSnid int32 //房主 delCards [][]int32 //已出牌 delOrders []int32 //出牌顺序 isKongBomb bool //是否空放,打出炸弹没人能接 bombToEnd int //空放炸弹致底分翻倍次数 card_play_action_seq []string //出牌历史记录 card_play_action_seq_int32 [][]int32 //出牌历史记录 lastPos int32 //前一个玩家位置 isAllRob bool //是否是纯AI场 robotGamingNum int // 机器人玩家数量 allPlayerCards [][]int32 //所有玩家的牌 recordId string // 牌局记录id testPokers []int32 // 发指定的牌,用于测试 cHintCards []int32 //客户端提示出牌记录 isCardsKu bool //是否是牌库 cardsKuId int32 //牌库ID ctrlType int // 1控赢 2控输 0不控 BilledList map[int32]*[]*BilledInfo // 多轮结算记录, 玩家id:每局结算记录 RoundEndTime []int64 // 每局结束时间 RoundLogId []string // 每局牌局记录id CustomLogSave bool // 是否已经保存日志 PlayerAward map[int32]*[]*model.Item // 房卡场最终奖励 } func NewTienLenSceneData(s *base.Scene) *TienLenSceneData { sceneEx := &TienLenSceneData{ Scene: s, poker: rule.NewPoker(), players: make(map[int32]*TienLenPlayerData), BilledList: map[int32]*[]*BilledInfo{}, PlayerAward: make(map[int32]*[]*model.Item), } sceneEx.Clear() return sceneEx } func (this *TienLenSceneData) init() bool { this.tianHuSnids = []int32{} this.winSnids = []int32{} this.roundScore = 0 this.currOpPos = rule.InvalidePos this.lastOpPos = rule.InvalidePos this.curBombPos = rule.InvalidePos this.lastBombPos = rule.InvalidePos this.lastPos = rule.InvalidePos this.UnmarkPass() this.curGamingPlayerNum = 0 this.curMinCard = 0 this.delCards = [][]int32{} this.delOrders = []int32{} this.card_play_action_seq = []string{} this.card_play_action_seq_int32 = [][]int32{} this.bombToEnd = 0 this.allPlayerCards = [][]int32{} this.ctrlType = 0 this.recordId, _ = model.AutoIncGameLogId() if this.GetPlayerNum() == 0 { this.SetPlayerNum(rule.MaxNumOfPlayer) } this.cHintCards = []int32{} return true } func (this *TienLenSceneData) Clear() { this.tianHuSnids = []int32{} this.winSnids = []int32{} this.roundScore = 0 this.currOpPos = rule.InvalidePos this.lastOpPos = rule.InvalidePos this.curBombPos = rule.InvalidePos this.lastBombPos = rule.InvalidePos this.lastPos = rule.InvalidePos this.curGamingPlayerNum = 0 this.curMinCard = 0 this.UnmarkPass() this.delCards = [][]int32{} this.delOrders = []int32{} this.card_play_action_seq = []string{} this.card_play_action_seq_int32 = [][]int32{} this.bombToEnd = 0 this.isAllRob = false this.robotGamingNum = 0 this.allPlayerCards = [][]int32{} this.ctrlType = 0 this.recordId, _ = model.AutoIncGameLogId() for _, player := range this.players { if player != nil { player.UnmarkFlag(base.PlayerState_WaitNext) } } this.cHintCards = []int32{} } func (this *TienLenSceneData) CanStart() bool { if this.GetGaming() == true { return false } nPlayerCount := this.GetPlayerCnt() nRobotCount := this.GetRobotCnt() if this.IsMatchScene() { if nRobotCount == 4 { this.isAllRob = true } if nPlayerCount == 4 { // 比赛场4人开赛 return true } else { return false } } if this.IsCustom() { return this.IsAllReady() && this.GetPlayerCnt() >= this.GetPlayerNum() } // 房间人数>=2开始,并且有真人或者是预创建房间,并且有房主 if nPlayerCount >= 2 && (this.GetRealPlayerNum() > 0 || this.IsPreCreateScene()) { //人数>=2开始 return true } return false } func (this *TienLenSceneData) GetPlayerCnt() int { var cnt int for i := 0; i < this.GetPlayerNum(); i++ { playerEx := this.seats[i] if playerEx == nil { continue } cnt++ } return cnt } func (this *TienLenSceneData) GetGameingPlayerCnt() int { var cnt int for i := 0; i < this.GetPlayerNum(); i++ { playerEx := this.seats[i] if playerEx == nil { continue } if playerEx.IsGameing() { cnt++ } } return cnt } func (this *TienLenSceneData) GetRobotCnt() int { var cnt int for i := 0; i < this.GetPlayerNum(); i++ { playerEx := this.seats[i] if playerEx == nil { continue } if playerEx.IsRob { cnt++ } } return cnt } func (this *TienLenSceneData) GetGameingRobotCnt() int { var cnt int for i := 0; i < this.GetPlayerNum(); i++ { playerEx := this.seats[i] if playerEx == nil { continue } if playerEx.IsRob && playerEx.IsGameing() { cnt++ } } return cnt } func (this *TienLenSceneData) GetSeatPlayerCnt() int { var cnt int for i := 0; i < this.GetPlayerNum(); i++ { playerEx := this.seats[i] if playerEx == nil { continue } cnt++ } return cnt } func (this *TienLenSceneData) BroadcastPlayerLeave(p *base.Player, reason int) { scLeavePack := &tienlen.SCTienLenPlayerLeave{ Pos: proto.Int(p.GetPos()), } proto.SetDefaults(scLeavePack) logger.Logger.Trace("SCTienLenPlayerLeave: ", p.SnId, " scLeavePack: ", scLeavePack) this.Broadcast(int(tienlen.TienLenPacketID_PACKET_SCTienLenPlayerLeave), scLeavePack, p.GetSid()) } func (this *TienLenSceneData) BroadcastAudienceNum(p *base.Player) { pack := &tienlen.SCTienLenUpdateAudienceNum{ AudienceNum: proto.Int(this.GetAudiencesNum()), } proto.SetDefaults(pack) this.Broadcast(int(tienlen.TienLenPacketID_PACKET_SCTienLenUpdateAudienceNum), pack, p.GetSid()) } func (this *TienLenSceneData) delPlayer(p *base.Player) { if p, exist := this.players[p.SnId]; exist { this.seats[p.GetPos()] = nil delete(this.players, p.SnId) } } func (this *TienLenSceneData) OnPlayerLeave(p *base.Player, reason int) { // 清除上局赢家id if this.lastWinSnid == p.SnId { this.lastWinSnid = 0 } this.delPlayer(p) this.BroadcastPlayerLeave(p, reason) if !p.IsRob { //真人离开 hadSeat := false for _, seat := range this.seats { if seat != nil { hadSeat = true break } } if !hadSeat { //座位上没有人了,把观众踢出去 audiences := this.GetAudiences() for _, audience := range audiences { if audience != nil { this.AudienceLeave(audience, common.PlayerLeaveReason_RoomClose) } } } } if !this.GetDestroyed() && this.IsCustom() && len(this.players) == 0 { this.SceneDestroy(true) } } func (this *TienLenSceneData) SceneDestroy(force bool) { this.SaveCustomLog() //销毁房间 this.Scene.Destroy(force) } // 广播房主更换 func (this *TienLenSceneData) BroadcastUpdateMasterSnid() { pack := &tienlen.SCTienLenUpdateMasterSnid{ MasterSnid: proto.Int32(this.masterSnid), } proto.SetDefaults(pack) this.Broadcast(int(tienlen.TienLenPacketID_PACKET_SCTienLenUpdateMasterSnid), pack, 0) logger.Logger.Trace("BroadcastUpdateMasterSnid ", pack) } // 广播当前操作的玩家位置 func (this *TienLenSceneData) BroadcastOpPos() { if this.currOpPos != rule.InvalidePos && this.seats[this.currOpPos] != nil && this.seats[this.currOpPos].IsRobot() { //计算其它人手牌,所有牌-出过的牌-自己手牌 othercards := []int32{} handcards := []int32{} /* for i := int32(0); i < 52; i++ { othercards = append(othercards, i) }*/ for _, cards := range this.allPlayerCards { for _, card := range cards { if card != -1 { othercards = append(othercards, tienlenApi.CardToAiCard[card]) } } } logger.Logger.Info("--------------------------------------othercards = ", othercards) card_play_action_seqs := []string{} for _, cards := range this.card_play_action_seq_int32 { //转棋牌到AI中的牌 aicards := []int32{} for _, card := range cards { aicards = append(aicards, tienlenApi.CardToAiCard[card]) } card_play_action_seqs = append(card_play_action_seqs, common.Int32SliceToString(aicards, ",")) othercards = common.DelSliceIn32s(othercards, aicards) } //删除手牌 for _, v := range this.seats[this.currOpPos].cards { if v != -1 { aicard := tienlenApi.CardToAiCard[v] othercards = common.DelSliceInt32(othercards, aicard) handcards = append(handcards, aicard) } } //logger.Logger.Infof("%v出牌历史记录:%v",this.currOpPos,this.card_play_action_seq) //logger.Logger.Infof("%v出牌历史记录:%v",this.currOpPos,common.StringSliceToString(card_play_action_seqs,"|")) //logger.Logger.Infof("%v手牌:%v 数量:%v",this.currOpPos,handcards,len(handcards)) //logger.Logger.Infof("%v其它人牌:%v 数量:%v",this.currOpPos,othercards,len(othercards)) lastmove := make(map[int][]int32) numCardsLeft := make(map[int]int) playedCards := make(map[int][]int32) for pos, v := range this.seats { if v != nil && v.IsGameing() { //出过的牌 delcards := []int32{} for _, cards := range v.delCards { for _, card := range cards { aicard := tienlenApi.CardToAiCard[card] delcards = append(delcards, aicard) } } //logger.Logger.Infof("%v出过的牌:%v",pos,delcards) //logger.Logger.Infof("%v剩余的牌:%v",pos,13-len(delcards)) playedCards[pos] = delcards numCardsLeft[pos] = 13 - len(delcards) last := len(v.delCards) if last > 0 { aicards := []int32{} for _, card := range v.delCards[last-1] { aicards = append(aicards, tienlenApi.CardToAiCard[card]) } //logger.Logger.Infof("%v最后一次出牌:%v",pos,aicards) lastmove[pos] = aicards } else { //logger.Logger.Infof("%v最后一次出牌:%v",pos,"") lastmove[pos] = []int32{} } } else { numCardsLeft[pos] = 0 } } isFirstHand := false if this.lastOpPos == rule.InvalidePos { //首出玩家 //有赢家,赢家先出,出牌不受限制 //无赢家,手持最小牌先出,最小牌必先出 winPos := this.FindWinPos() if winPos == -1 { //无赢家 isFirstHand = true } } isWin := true B := int32(0) for _, seat := range this.seats { if seat != nil && seat.IsGameing() { if !seat.IsRob { seat.odds = this.GetPlayerOdds(seat.Player, int(this.GameId), this.robotGamingNum > 0) if seat.odds < 0 { B -= seat.odds } } } } if B < 0 { isWin = false } isTienLenYule := this.IsTienLenYule() pack := &tienlen.SCTienLenAIData{ BombNum: 0, //炸弹数量 CardPlayActionSeq: proto.String(strings.Replace(common.StringSliceToString(card_play_action_seqs, "|"), "-1", "", -1)), LastMove_0: proto.String(common.Int32SliceToString(lastmove[0], ",")), LastMove_1: proto.String(common.Int32SliceToString(lastmove[1], ",")), LastMove_2: proto.String(common.Int32SliceToString(lastmove[2], ",")), LastMove_3: proto.String(common.Int32SliceToString(lastmove[3], ",")), NumCardsLeft_0: proto.Int32(int32(numCardsLeft[0])), NumCardsLeft_1: proto.Int32(int32(numCardsLeft[1])), NumCardsLeft_2: proto.Int32(int32(numCardsLeft[2])), NumCardsLeft_3: proto.Int32(int32(numCardsLeft[3])), OtherHandCards: proto.String(common.Int32SliceToString(othercards, ",")), PlayedCards_0: proto.String(common.Int32SliceToString(playedCards[0], ",")), PlayedCards_1: proto.String(common.Int32SliceToString(playedCards[1], ",")), PlayedCards_2: proto.String(common.Int32SliceToString(playedCards[2], ",")), PlayedCards_3: proto.String(common.Int32SliceToString(playedCards[3], ",")), PlayerHandCards: proto.String(common.Int32SliceToString(handcards, ",")), PlayerPosition: proto.Int32(this.currOpPos), IsTienLenYule: proto.Bool(isTienLenYule), IsFirstHand: proto.Bool(isFirstHand), CardsLeft_0: this.GetPlayerCards(0), CardsLeft_1: this.GetPlayerCards(1), CardsLeft_2: this.GetPlayerCards(2), CardsLeft_3: this.GetPlayerCards(3), LastPos: proto.Int32(this.lastOpPos), IsEnd: this.IsTienLenToEnd(), WinSnids: this.winSnids, IsWin: isWin, } proto.SetDefaults(pack) this.seats[this.currOpPos].SendToClient(int(tienlen.TienLenPacketID_PACKET_SCTienLenAI), pack) logger.Logger.Infof("--------------------Send Robot AI Data:%v", pack) } pack := &tienlen.SCTienLenCurOpPos{ Pos: proto.Int32(this.currOpPos), IsNew: proto.Bool(this.currOpPos == this.lastOpPos || this.lastOpPos == rule.InvalidePos), } lastOpPlayer := this.GetLastOpPlayer() if lastOpPlayer != nil && this.currOpPos != this.lastOpPos { if len(lastOpPlayer.delCards) > 0 { lastDelCards := lastOpPlayer.delCards[len(lastOpPlayer.delCards)-1] for _, card := range lastDelCards { if card != rule.InvalideCard { pack.Cards = append(pack.Cards, card) } } } } if this.lastPos != rule.InvalidePos && this.lastOpPos == this.lastPos { // 特殊牌型才去添加额外延迟时间 if rule.NeedExDelay(pack.Cards) { pack.ExDelay = proto.Int32(int32(rule.DelayCanOp)) } } proto.SetDefaults(pack) this.Broadcast(int(tienlen.TienLenPacketID_PACKET_SCTienLenCurOpPos), pack, 0) logger.Logger.Trace("(this *TienLenSceneData) BroadcastOpPos TienLenPacketID_PACKET_SCTienLenCurOpPos", this.GetSceneId(), ";pack:", pack) } // 获取玩家剩余的牌 func (this *TienLenSceneData) GetPlayerCards(pos int32) []int32 { data := []int32{} if len(this.allPlayerCards)-1 < int(pos) { return data } cards := this.allPlayerCards[pos] //去除值为-1的牌 for _, card := range cards { if card != -1 { data = append(data, card) } } return data } // 出牌 func (this *TienLenSceneData) DelCards(playerEx *TienLenPlayerData, cards []int32) bool { if playerEx != nil && len(cards) != 0 { cs := make([]int32, len(cards)) copy(cs, cards) for _, delCard := range cs { for i, hcard := range playerEx.cards { if delCard == hcard && hcard != rule.InvalideCard { playerEx.cards[i] = rule.InvalideCard continue } } } rule.MinSortCards(cs) playerEx.delCards = append(playerEx.delCards, cs) this.delCards = append(this.delCards, cs) this.delOrders = append(this.delOrders, playerEx.SnId) this.card_play_action_seq = append(this.card_play_action_seq, fmt.Sprintf("%v-%v", playerEx.GetPos(), common.Int32SliceToString(cs, ","))) this.card_play_action_seq_int32 = append(this.card_play_action_seq_int32, cs) return true } return false } func (this *TienLenSceneData) PlayerCanOp(pos int32) bool { if pos < 0 || pos >= int32(this.GetPlayerNum()) { return false } if this.seats[pos] != nil && this.seats[pos].CanOp() { return true } this.card_play_action_seq = append(this.card_play_action_seq, fmt.Sprintf("%v-过", pos)) this.card_play_action_seq_int32 = append(this.card_play_action_seq_int32, []int32{-1}) return false } func (this *TienLenSceneData) AllPlayerEnterGame() { for i := 0; i < this.GetPlayerNum(); i++ { if this.seats[i] == nil { continue } if this.seats[i].IsMarkFlag(base.PlayerState_GameBreak) == false { this.seats[i].UnmarkFlag(base.PlayerState_WaitNext) this.seats[i].SyncFlag() } } } // 娱乐版 func (this *TienLenSceneData) IsTienLenYule() bool { return common.IsTienLenYuLe(this.GetGameId()) } // 打到底 func (this *TienLenSceneData) IsTienLenToEnd() bool { return common.IsTienLenToEnd(this.GetGameId()) } func (this *TienLenSceneData) GetFreeGameSceneType() int32 { return this.GetSceneType() } // 比赛场发牌 // 纯真人,随机发牌 // 有机器人和真人,真人拿好牌 func (this *TienLenSceneData) SendHandCard_Match() { this.poker.Shuffle() buf := this.poker.GetPokerBuf() cardss := map[int][]int32{} for i, card := range buf { if int32(card) != rule.InvalideCard { index := i / 13 cardss[index] = append(cardss[index], int32(card)) } } realPlayers := []*TienLenPlayerData{} robotPlayers := []*TienLenPlayerData{} for _, seat := range this.seats { if seat != nil && seat.IsGameing() { if seat.IsRob { robotPlayers = append(robotPlayers, seat) } else { realPlayers = append(realPlayers, seat) } } } if len(realPlayers) > 0 && len(robotPlayers) > 0 { type gradeInfo struct { id int grade int cards []int32 } grades := []gradeInfo{} for i, card13 := range cardss { cardTmp := make([]int32, 13) copy(cardTmp, card13) grade := rule.GetCardsGrade(cardTmp, this.IsTienLenYule()) gi := gradeInfo{ id: i, grade: grade, cards: card13, } grades = append(grades, gi) } realWin := 1 //-1最差 0正常 1最好 switch realWin { case -1: sort.Slice(grades, func(i, j int) bool { return grades[i].grade > grades[j].grade }) case 0: case 1: sort.Slice(grades, func(i, j int) bool { return grades[i].grade < grades[j].grade }) } for i, gi := range grades { cardss[i] = gi.cards } } minCard := int32(999) if len(robotPlayers) > 0 { for i, seat := range robotPlayers { for index, card := range cardss[i] { seat.cards[index] = card if rule.Value(card) < rule.Value(minCard) { this.startOpPos = int32(seat.GetPos()) minCard = card this.curMinCard = minCard } else if rule.Value(card) == rule.Value(minCard) { if rule.Color(card) < rule.Color(minCard) { this.startOpPos = int32(seat.GetPos()) minCard = card this.curMinCard = minCard } } } cardTmp := make([]int32, 13) copy(cardTmp, cardss[i]) grade := rule.GetCardsGrade(cardTmp, this.IsTienLenYule()) logger.Logger.Trace("SnId: ", seat.SnId, ";grade: ", grade) pack := &tienlen.SCTienLenCard{} for j := int32(0); j < rule.HandCardNum; j++ { pack.Cards = append(pack.Cards, seat.cards[j]) } pack.IsOutRecord = seat.CanUseRecordItem() proto.SetDefaults(pack) seat.SendToClient(int(tienlen.TienLenPacketID_PACKET_SCTienLenCard), pack) logger.Logger.Trace("SnId: ", seat.SnId, ";SCTienLenCard: ", pack.Cards) pack.SnId = seat.SnId this.BroadcastToAudience(int(tienlen.TienLenPacketID_PACKET_SCTienLenCard), pack) } } if len(realPlayers) > 0 { for i, seat := range realPlayers { for index, card := range cardss[len(robotPlayers)+i] { seat.cards[index] = card if rule.Value(card) < rule.Value(minCard) { this.startOpPos = int32(seat.GetPos()) minCard = card this.curMinCard = minCard } else if rule.Value(card) == rule.Value(minCard) { if rule.Color(card) < rule.Color(minCard) { this.startOpPos = int32(seat.GetPos()) minCard = card this.curMinCard = minCard } } } cardTmp := make([]int32, 13) copy(cardTmp, cardss[len(robotPlayers)+i]) grade := rule.GetCardsGrade(cardTmp, this.IsTienLenYule()) logger.Logger.Trace("SnId: ", seat.SnId, ";grade: ", grade) pack := &tienlen.SCTienLenCard{} for j := int32(0); j < rule.HandCardNum; j++ { pack.Cards = append(pack.Cards, seat.cards[j]) } pack.IsOutRecord = seat.CanUseRecordItem() proto.SetDefaults(pack) seat.SendToClient(int(tienlen.TienLenPacketID_PACKET_SCTienLenCard), pack) logger.Logger.Trace("SnId: ", seat.SnId, ";SCTienLenCard: ", pack.Cards) pack.SnId = seat.SnId this.BroadcastToAudience(int(tienlen.TienLenPacketID_PACKET_SCTienLenCard), pack) } } } // Shuffle 发牌 // offGrade 需要牌分最少差值(非打到底牌分最大的一组牌和最小的一组牌的分差;打到底,分数最少的两组牌的分差) // num 函数内部使用,限制搜索次数 // maxOff 函数内部使用 func (this *TienLenSceneData) Shuffle(offGrade, num, maxOff int) { this.poker.Shuffle() buf := this.poker.GetPokerBuf() cardss := map[int][]int32{} for i, card := range buf { if int32(card) != rule.InvalideCard { index := i / 13 cardss[index] = append(cardss[index], int32(card)) } } type gradeInfo struct { id int grade int cards []int32 } grades := []gradeInfo{} for i, card13 := range cardss { cardTmp := make([]int32, 13) copy(cardTmp, card13) cardsTypeMap := rule.GetCardsType(cardTmp, this.IsTienLenYule()) grade, _ := rule.GetCardTypeScore(cardsTypeMap, this.IsTienLenYule(), cardTmp) gi := gradeInfo{ id: i, grade: grade, cards: card13, } grades = append(grades, gi) } sort.Slice(grades, func(i, j int) bool { return grades[i].grade < grades[j].grade }) if grades[len(grades)-1].grade == 9999 && !this.poker.IsTianhuPoker() { //天胡 this.poker.TianhuPokerBuf() } if this.IsTienLenToEnd() { //所有值都比最小值大offGrade分 off := grades[1].grade - grades[0].grade if off > maxOff { maxOff = off this.poker.CopyPokerBuf() } if offGrade == 0 || off >= offGrade || num > 10000 { if num > 10000 { if this.poker.IsTianhuPoker() && rand.Intn(100) < 20 { this.poker.UnTianhuPokerBuf() } else { this.poker.UncopyPokerBuf() } } return } } else { //最大值比最小值大offGrade分 off := grades[len(grades)-1].grade - grades[0].grade if off > maxOff && grades[len(grades)-1].grade != 9999 { maxOff = off this.poker.CopyPokerBuf() } if offGrade == 0 || off >= offGrade || num > 10000 { if num > 10000 { if this.poker.IsTianhuPoker() && rand.Intn(100) < 20 { this.poker.UnTianhuPokerBuf() } else { this.poker.UncopyPokerBuf() } } return } } num++ this.Shuffle(offGrade, num, maxOff) } /* 公共房:赔率发牌 私人房:随机发牌 比赛场: 纯真人,随机发牌 有机器人和真人,真人拿好牌 纯机器人,随机发牌 赔率发牌 1.52张牌随机分成4组(评估牌好坏) 2.找出个人赔率最低的玩家 3.只有一个真实玩家 1.该玩家个人赔率≤80%,30%概率给予玩家最好牌,70%概率随机发牌 2.该玩家 80%<个人赔率≤95%,随机发牌 3.该玩家 95%<个人赔率≤98%,50%概率给予玩家最小牌,50%概率随机发牌 4.该玩家 个人赔率>98%,给予玩家最小牌,切保证最大牌型和最小牌型之间的分差≥5(代码里对应50) 4.多个真实玩家 1.纯真人 50%概率给予最大牌,50%概率随机发牌 2.有机器人 1.该玩家个人赔率≤90%,50%概率给予玩家最好牌,50%概率随机发牌 2.该玩家 90%<个人赔率>98%,随机发牌 3.该玩家 个人赔率>98%,给予机器人最大牌 5.根据赔率最低玩家发牌,如果被控人拿最小牌,机器人拿最大牌,其余玩家随机发牌 */ // SendHandCard_LoseRate 按照赔率发牌 //func (this *TienLenSceneData) SendHandCard_LoseRate() { // var minLoseRateSnid int32 //snid // var realPlayerType int //-1最小牌 0正常随机 1最大牌 // var offGrade int //需求分值差 // // realPlayers := []*TienLenPlayerData{} // robotPlayers := []*TienLenPlayerData{} // for _, seat := range this.seats { // if seat != nil && seat.IsGameing() { // if seat.IsRob { // robotPlayers = append(robotPlayers, seat) // } else { // realPlayers = append(realPlayers, seat) // } // } // } // if len(realPlayers) > 0 { // //找出个人赔率最低的玩家 // minLoseRate := realPlayers[0].LoseRate(this.GetGameFreeId(), int32(this.GetGameId())) // minLoseRateSnid = realPlayers[0].SnId // for _, player := range realPlayers { // loseRate := player.LoseRate(this.GetGameFreeId(), int32(this.GetGameId())) // logger.Logger.Trace("snid: ", player.SnId, " loseRate: ", loseRate) // if loseRate < minLoseRate { // minLoseRate = loseRate // minLoseRateSnid = player.SnId // } // } // if !this.IsMatchScene() && !this.IsPrivateScene() { //比赛场或者私有房间纯随机 // //一个真实玩家 // if len(realPlayers) == 1 { // //1.该玩家个人赔率≤80%,30%概率给予玩家最好牌,70%概率随机发牌 // if minLoseRate <= 0.8 { // if rand.Int31n(100) < 30 { // realPlayerType = 1 // } else { // realPlayerType = 0 // } // } else if minLoseRate <= 0.95 { //2.该玩家 80%<个人赔率≤95%,随机发牌 // realPlayerType = 0 // } else if minLoseRate <= 0.98 { //3.该玩家 95%<个人赔率≤98%,50%概率给予玩家最小牌,50%概率随机发牌 // if rand.Int31n(100) < 50 { // realPlayerType = -1 // } else { // realPlayerType = 0 // } // } else { //3.该玩家 个人赔率>98%,给予玩家最小牌,切保证最大牌型和最小牌型之间的分差≥5(代码里对应50) // realPlayerType = -1 // offGrade = 40 // } // } else { //多个真人 // if len(robotPlayers) == 0 { //纯真人 // if this.IsPrivateScene() { //私有房间纯随机 // realPlayerType = 0 // } else { // if rand.Int31n(100) < 50 { //50%概率给予最大牌,50%概率随机发牌 // realPlayerType = 1 // } else { // realPlayerType = 0 // } // } // } else { // //1.该玩家个人赔率≤90%,50%概率给予玩家最好牌,50%概率随机发牌 // if minLoseRate <= 0.9 { // if rand.Int31n(100) < 50 { // realPlayerType = 1 // } else { // realPlayerType = 0 // } // } else if minLoseRate < 0.98 { //2.该玩家 90%<个人赔率>98%,随机发牌 // realPlayerType = 0 // } else { //3.该玩家 个人赔率>98%,给予机器人最大牌 // realPlayerType = -1 // } // } // } // } // } // // this.Shuffle(offGrade, 0, 0) // // buf := this.poker.GetPokerBuf() // cardss := map[int][]int32{} // for i, card := range buf { // if int32(card) != rule.InvalideCard { // index := i / 13 // cardss[index] = append(cardss[index], int32(card)) // } // } // // type gradeInfo struct { // id int // grade int // cards []int32 // } // grades := []gradeInfo{} // for i, card13 := range cardss { // cardTmp := make([]int32, 13) // copy(cardTmp, card13) // grade := rule.GetCardsGrade(cardTmp, this.IsTienLenYule()) // gi := gradeInfo{ // id: i, // grade: grade, // cards: card13, // } // grades = append(grades, gi) // } // if realPlayerType != 0 { // sort.Slice(grades, func(i, j int) bool { // return grades[i].grade > grades[j].grade // }) // } // // for i, gi := range grades { // cardss[i] = gi.cards // } // // Grades := make(map[int32]int) // minCard := int32(999) // // 被控人 // for snid, player := range this.players { // if player.IsGameing() && snid == minLoseRateSnid { // cards := cardss[0] //最大 // if realPlayerType == -1 { // cards = cardss[len(cardss)-1] //最小 // delete(cardss, len(cardss)-1) // } else { // delete(cardss, 0) // } // switch realPlayerType { // case -1: // logger.Logger.Trace("player get min grade !") // case 0: // logger.Logger.Trace("player get rand grade !") // case 1: // logger.Logger.Trace("player get max grade !") // } // for index, card := range cards { // player.cards[index] = card // if rule.Value(card) < rule.Value(minCard) { // this.startOpPos = int32(player.GetPos()) // minCard = card // this.curMinCard = minCard // } else if rule.Value(card) == rule.Value(minCard) { // if rule.Color(card) < rule.Color(minCard) { // this.startOpPos = int32(player.GetPos()) // minCard = card // this.curMinCard = minCard // } // } // } // // cardTmp := make([]int32, 13) // copy(cardTmp, cards) // grade := rule.GetCardsGrade(cardTmp, this.IsTienLenYule()) // logger.Logger.Trace("SnId: ", player.SnId, ";grade: ", grade) // Grades[player.SnId] = grade // // pack := &tienlen.SCTienLenCard{} // for j := int32(0); j < rule.HandCardNum; j++ { // pack.Cards = append(pack.Cards, player.cards[j]) // } // proto.SetDefaults(pack) // player.SendToClient(int(tienlen.TienLenPacketID_PACKET_SCTienLenCard), pack) // logger.Logger.Trace("SnId: ", player.SnId, ";SCTienLenCard: ", pack) // break // } // } // if realPlayerType == -1 { //被控人拿最小牌,机器人拿最大牌 // snids := []int32{} // for snid, player := range this.players { // if player.IsRob && player.IsGameing() && snid != minLoseRateSnid && player.cards[0] == rule.InvalideCard { // snids = append(snids, snid) // } // } // if len(snids) > 0 { // snid := snids[rand.Intn(len(snids))] // player := this.players[snid] // if player != nil { // cards := cardss[0] //最大 // delete(cardss, 0) // for index, card := range cards { // player.cards[index] = card // if rule.Value(card) < rule.Value(minCard) { // this.startOpPos = int32(player.GetPos()) // minCard = card // this.curMinCard = minCard // } else if rule.Value(card) == rule.Value(minCard) { // if rule.Color(card) < rule.Color(minCard) { // this.startOpPos = int32(player.GetPos()) // minCard = card // this.curMinCard = minCard // } // } // } // // cardTmp := make([]int32, 13) // copy(cardTmp, cards) // grade := rule.GetCardsGrade(cardTmp, this.IsTienLenYule()) // logger.Logger.Trace("SnId: ", player.SnId, ";grade: ", grade) // Grades[player.SnId] = grade // // pack := &tienlen.SCTienLenCard{} // for j := int32(0); j < rule.HandCardNum; j++ { // pack.Cards = append(pack.Cards, player.cards[j]) // } // proto.SetDefaults(pack) // player.SendToClient(int(tienlen.TienLenPacketID_PACKET_SCTienLenCard), pack) // logger.Logger.Trace("SnId: ", player.SnId, ";SCTienLenCard: ", pack) // } // } // } // for _, cards := range cardss { // for snid, player := range this.players { // if player.IsGameing() && snid != minLoseRateSnid && player.cards[0] == rule.InvalideCard { // for index, card := range cards { // player.cards[index] = card // if rule.Value(card) < rule.Value(minCard) { // this.startOpPos = int32(player.GetPos()) // minCard = card // this.curMinCard = minCard // } else if rule.Value(card) == rule.Value(minCard) { // if rule.Color(card) < rule.Color(minCard) { // this.startOpPos = int32(player.GetPos()) // minCard = card // this.curMinCard = minCard // } // } // } // // cardTmp := make([]int32, 13) // copy(cardTmp, cards) // grade := rule.GetCardsGrade(cardTmp, this.IsTienLenYule()) // logger.Logger.Trace("SnId: ", player.SnId, ";grade: ", grade) // Grades[player.SnId] = grade // // pack := &tienlen.SCTienLenCard{} // for j := int32(0); j < rule.HandCardNum; j++ { // pack.Cards = append(pack.Cards, player.cards[j]) // } // proto.SetDefaults(pack) // player.SendToClient(int(tienlen.TienLenPacketID_PACKET_SCTienLenCard), pack) // logger.Logger.Trace("SnId: ", player.SnId, ";SCTienLenCard: ", pack) // break // } // } // } // // // 测试数据 // for snid, player := range this.players { // if player.IsGameing() && !player.IsRob { // pack := &tienlen.SCTienLenCardTest{} // if snid == minLoseRateSnid { // switch realPlayerType { // case -1: // pack.Type = 1 // case 0: // pack.Type = 2 // case 1: // pack.Type = 3 // } // } // pack.Totalout, pack.Totalin = player.TotalOutIn(int32(this.GetGameId())) // pack.LoseRate = player.LoseRate(this.GetGameFreeId(), int32(this.GetGameId())) // if Grades != nil { // pack.Grades = make(map[int32]int32) // for id, grade := range Grades { // pack.Grades[id] = int32(grade) // } // } // proto.SetDefaults(pack) // player.SendToClient(int(tienlen.TienLenPacketID_PACKET_SCTienLenCardTest), pack) // logger.Logger.Trace("SnId: ", player.SnId, ";SCTienLenCardTest: ", pack) // } // } //} // SendHandCardOdds 调控发牌 /* 1.计算玩家调控概率 新手天胡体验发牌 end 2.根据玩家调控概率随机获得需要控输或控赢的玩家 1.有控输玩家 1.机器人发天胡 end 2.分差发牌,机器人发好牌 end 2.有控赢,给控赢的玩家发好牌,其余随机发牌 end */ func (this *TienLenSceneData) SendHandCardOdds() { var realPlayersGood, realPlayersBad, realPlayers, robotPlayers, novicePlayers, notNoviceRealPlayers []*TienLenPlayerData var G, B int32 for _, seat := range this.seats { if seat != nil && seat.IsGameing() { if seat.IsRob { robotPlayers = append(robotPlayers, seat) } else { seat.odds = this.GetPlayerOdds(seat.Player, int(this.GameId), this.robotGamingNum > 0) seat.playerPool = int(this.PlayerPoolOdds(seat.Player)) if seat.odds > 0 { realPlayersGood = append(realPlayersGood, seat) G += seat.odds } else if seat.odds < 0 { realPlayersBad = append(realPlayersBad, seat) B -= seat.odds } else { realPlayers = append(realPlayers, seat) } _, isNovice := seat.NoviceOdds(int(this.GameId)) if isNovice { novicePlayers = append(novicePlayers, seat) } else { notNoviceRealPlayers = append(notNoviceRealPlayers, seat) } } } } var Grades = make(map[int32]int32) var minCard = int32(999) f1 := func(p *TienLenPlayerData, cards []int32) { // 发牌方法 cardsTypeMap := rule.GetCardsType(cards, this.IsTienLenYule()) score, _ := rule.GetCardTypeScore(cardsTypeMap, this.IsTienLenYule(), cards) Grades[p.SnId] = int32(score) // 测试用 for index, card := range cards { p.cards[index] = card if rule.Value(card) < rule.Value(minCard) { this.startOpPos = int32(p.GetPos()) minCard = card this.curMinCard = minCard } else if rule.Value(card) == rule.Value(minCard) { if rule.Color(card) < rule.Color(minCard) { this.startOpPos = int32(p.GetPos()) minCard = card this.curMinCard = minCard } } } pack := &tienlen.SCTienLenCard{} for j := int32(0); j < rule.HandCardNum; j++ { pack.Cards = append(pack.Cards, p.cards[j]) } pack.IsOutRecord = p.CanUseRecordItem() proto.SetDefaults(pack) p.SendToClient(int(tienlen.TienLenPacketID_PACKET_SCTienLenCard), pack) logger.Logger.Trace("SnId: ", p.SnId, ";SCTienLenCard: ", pack) } // 新手天胡体验 // 前两局不能天胡,之后根据概率给一次天胡,至少天胡一次 noviceTianHu := false noviceCtrl := false //config := srvdata.PBDB_NewPlayerMgr.GetData(int32(this.GameId)) //if config != nil && config.GetTianHuRate() > 0 && len(this.testPokers) == 0 { // 启用新手天胡体验 // rand.Shuffle(len(novicePlayers), func(i, j int) { // novicePlayers[i], novicePlayers[j] = novicePlayers[j], novicePlayers[i] // }) // keyNovice := common.GetKeyNoviceGameId(this.GameId) // for _, v := range novicePlayers { // data, ok := v.GDatas[keyNovice] // if !ok { // data = &model.PlayerGameInfo{FirstTime: time.Now()} // v.GDatas[keyNovice] = data // } // // 前两局不能天胡 // if data.Statics.GameTimes < 2 { // continue // } // if int(config.GetTianHuRate()) > this.RandInt(1000) { // var allcs []int32 // for i := 0; i < 52; i++ { // allcs = append(allcs, int32(i)) // } // rand.Shuffle(len(allcs), func(i, j int) { // allcs[i], allcs[j] = allcs[j], allcs[i] // }) // cs, _ := rule.GetTianHu() // allcs = common.DelSliceIn32s(allcs, cs) // for _, seat := range this.seats { // if seat != nil && seat.IsGameing() { // if seat != v { // f1(seat, allcs[:13]) // allcs = allcs[13:] // } else { // f1(seat, cs) // } // } // } // v.TestLog = append(v.TestLog, fmt.Sprintf("新手天胡体验,玩家:%d 发天胡 num:%v", v.SnId, data.Statics.GameTimes)) // logger.Logger.Tracef("新手天胡体验,玩家:%d 发天胡", v.SnId) // noviceTianHu = true // noviceCtrl = true // } // } // if !noviceTianHu { // // 没有新手玩家天胡,随机一个非新手玩家,如果没有天胡过,给一次天胡 // rand.Shuffle(len(notNoviceRealPlayers), func(i, j int) { // notNoviceRealPlayers[i], notNoviceRealPlayers[j] = notNoviceRealPlayers[j], notNoviceRealPlayers[i] // }) // for _, v := range notNoviceRealPlayers { // data, ok := v.GDatas[keyNovice] // if !ok { // data = &model.PlayerGameInfo{FirstTime: time.Now()} // v.GDatas[keyNovice] = data // } // // if data.Statics.GetInt(rule.StaticsTianHuTimes) == 0 { // var allcs []int32 // for i := 0; i < 52; i++ { // allcs = append(allcs, int32(i)) // } // rand.Shuffle(len(allcs), func(i, j int) { // allcs[i], allcs[j] = allcs[j], allcs[i] // }) // cs, _ := rule.GetTianHu() // allcs = common.DelSliceIn32s(allcs, cs) // for _, seat := range this.seats { // if seat != nil && seat.IsGameing() { // if seat != v { // f1(seat, allcs[:13]) // allcs = allcs[13:] // } else { // f1(seat, cs) // } // } // } // v.TestLog = append(v.TestLog, fmt.Sprintf("新手阶段没有天胡过,玩家:%d 发天胡 num:%v", v.SnId, data.Statics.GameTimes)) // logger.Logger.Tracef("新手阶段没有天胡过,玩家:%v 发天胡", v.SnId) // noviceCtrl = true // } // } // } //} // 全局配置 gameConfig := base.ConfigMgrInst.GetConfig(this.Platform).GameConfig // testPokers 用于测试 isTestPoker := false if len(this.testPokers) > 1 { var allcs []int32 for i := 0; i < 52; i++ { allcs = append(allcs, int32(i)) } rand.Shuffle(len(allcs), func(i, j int) { allcs[i], allcs[j] = allcs[j], allcs[i] }) if len(this.testPokers) > 14 { this.testPokers = this.testPokers[:14] } if len(this.testPokers) < 14 { allcs = common.DelSliceIn32s(allcs, this.testPokers[1:]) this.testPokers = append(this.testPokers, allcs[:14-len(this.testPokers)]...) allcs = allcs[14-len(this.testPokers):] } allcs = common.DelSliceIn32s(allcs, this.testPokers[1:]) for _, seat := range this.seats { if seat != nil && seat.IsGameing() { if seat.SnId == this.testPokers[0] { f1(seat, this.testPokers[1:]) } else { f1(seat, allcs[:13]) allcs = allcs[13:] } } } this.testPokers = nil isTestPoker = true } // testPokers // 需要换牌 isGood := len(realPlayersGood) > 0 && int32(this.RandInt(1000)) < G isBad := len(realPlayersBad) > 0 && int32(this.RandInt(1000)) < B logger.Logger.Tracef("TienLen SendHandCardOdds Good:%v G:%v Bad:%v B:%v", isGood, G, isBad, B) if isBad && !isTestPoker && len(robotPlayers) > 0 && !noviceCtrl && !noviceTianHu { this.ctrlType = 2 gf := base.ConfigMgrInst.GetConfig(this.Platform).GameConfig items := []int32{gf.GetTianHu(), gf.GetPaiKu(), gf.GetFenCha()} score := this.RandInt(int(gf.GetTianHu() + gf.GetPaiKu() + gf.GetFenCha())) sum := int32(0) tp := 0 for k, v := range items { sum += v if int32(score) < sum { tp = k break } } logger.Logger.Tracef("TienLen SendHandCardOdds TianHu:%v PaiKu:%v FenCha:%v, tp:%v", items[0], items[1], items[2], tp) switch tp { case 0: // 天胡发牌 logger.Logger.Tracef("TienLen SendHandCardOdds TianHuRate") p := robotPlayers[0] th, _ := rule.GetTianHu() if len(th) == rule.Hand_CardNum { // 机器人发天胡,其它玩家随机发牌 f1(p, th) var allCards []int32 for i := 0; i < rule.POKER_CNT; i++ { allCards = append(allCards, int32(i)) } allCards = common.DelSliceIn32s(allCards, th) rand.Shuffle(len(allCards), func(i, j int) { allCards[i], allCards[j] = allCards[j], allCards[i] }) for _, v := range this.players { // map随机 if v == nil || !v.IsGameing() || v.cards[0] != rule.InvalideCard { continue } f1(v, allCards[:rule.Hand_CardNum]) allCards = allCards[rule.Hand_CardNum:] } } p.TestLog = append(p.TestLog, fmt.Sprintf("天胡发牌 snid%v 权重%v", p.SnId, gameConfig.GetTianHu())) case 1: // 牌库发牌 logger.Logger.Tracef("TienLen SendHandCardOdds 牌库") //todo 牌库 random := rand.Intn(10000) + 1 var cardsArr [][]int32 this.isCardsKu = true this.cardsKuId = int32(random) if this.IsTienLenYule() { cardsPool := srvdata.PBDB_CardsYuLeMgr.GetData(int32(random)) logger.Logger.Tracef("娱乐牌库发牌!!!!!! 随机到的牌库id = %d\n,db_CardsYuLe = %v\n", cardsPool.Id, cardsPool) var numbers []int32 err := json.Unmarshal([]byte(cardsPool.Card1), &numbers) if err != nil { fmt.Println("JSON unmarshaling failed:", err) return } cardsArr = append(cardsArr, numbers) numbers = []int32{} err = json.Unmarshal([]byte(cardsPool.Card2), &numbers) if err != nil { fmt.Println("JSON unmarshaling failed:", err) return } cardsArr = append(cardsArr, numbers) numbers = []int32{} err = json.Unmarshal([]byte(cardsPool.Card3), &numbers) if err != nil { fmt.Println("JSON unmarshaling failed:", err) return } cardsArr = append(cardsArr, numbers) numbers = []int32{} err = json.Unmarshal([]byte(cardsPool.Card4), &numbers) if err != nil { fmt.Println("JSON unmarshaling failed:", err) return } cardsArr = append(cardsArr, numbers) //排序 sort.Slice(cardsArr, func(i, j int) bool { cardsTypeMap := rule.GetCardsType(cardsArr[i], this.IsTienLenYule()) score1, _ := rule.GetCardTypeScore(cardsTypeMap, this.IsTienLenYule(), cardsArr[i]) cardsTypeMap2 := rule.GetCardsType(cardsArr[j], this.IsTienLenYule()) score2, _ := rule.GetCardTypeScore(cardsTypeMap2, this.IsTienLenYule(), cardsArr[j]) return score1 > score2 }) if isGood { sort.Slice(realPlayersGood, func(i, j int) bool { return realPlayersGood[i].odds > realPlayersGood[j].odds }) for _, v := range realPlayersGood { f1(v, cardsArr[0]) cardsArr = cardsArr[1:] } } if isBad { sort.Slice(realPlayersBad, func(i, j int) bool { return realPlayersBad[i].odds < realPlayersBad[j].odds }) for _, v := range realPlayersBad { f1(v, cardsArr[len(cardsArr)-1]) cardsArr = cardsArr[:len(cardsArr)-1] } } //机器人发牌和不调控的人 for _, v := range append(robotPlayers, realPlayers...) { f1(v, cardsArr[0]) cardsArr = cardsArr[1:] } } else { cardsPool := srvdata.PBDB_CardsJDMgr.GetData(int32(random)) logger.Logger.Tracef("经典牌库发牌!!!!!! 随机到的牌库id = %d\n,db_CardsYuLe = %v\n", cardsPool.Id, cardsPool) var numbers []int32 err := json.Unmarshal([]byte(cardsPool.Card1), &numbers) if err != nil { fmt.Println("JSON unmarshaling failed:", err) return } cardsArr = append(cardsArr, numbers) numbers = []int32{} err = json.Unmarshal([]byte(cardsPool.Card2), &numbers) if err != nil { fmt.Println("JSON unmarshaling failed:", err) return } cardsArr = append(cardsArr, numbers) numbers = []int32{} err = json.Unmarshal([]byte(cardsPool.Card3), &numbers) if err != nil { fmt.Println("JSON unmarshaling failed:", err) return } cardsArr = append(cardsArr, numbers) numbers = []int32{} err = json.Unmarshal([]byte(cardsPool.Card4), &numbers) if err != nil { fmt.Println("JSON unmarshaling failed:", err) return } cardsArr = append(cardsArr, numbers) //排序 sort.Slice(cardsArr, func(i, j int) bool { cardsTypeMap := rule.GetCardsType(cardsArr[i], this.IsTienLenYule()) score1, _ := rule.GetCardTypeScore(cardsTypeMap, this.IsTienLenYule(), cardsArr[i]) cardsTypeMap2 := rule.GetCardsType(cardsArr[j], this.IsTienLenYule()) score2, _ := rule.GetCardTypeScore(cardsTypeMap2, this.IsTienLenYule(), cardsArr[j]) return score1 > score2 }) if isGood { sort.Slice(realPlayersGood, func(i, j int) bool { return realPlayersGood[i].odds > realPlayersGood[j].odds }) for _, v := range realPlayersGood { f1(v, cardsArr[0]) cardsArr = cardsArr[1:] } } if isBad { sort.Slice(realPlayersBad, func(i, j int) bool { return realPlayersBad[i].odds < realPlayersBad[j].odds }) for _, v := range realPlayersBad { f1(v, cardsArr[len(cardsArr)-1]) cardsArr = cardsArr[:len(cardsArr)-1] } } //机器人发牌和不调控的人 for _, v := range append(robotPlayers, realPlayers...) { f1(v, cardsArr[0]) cardsArr = cardsArr[1:] } } case 2: // 分差发牌 logger.Logger.Tracef("TienLen SendHandCardOdds 分差发牌") for i := 0; i <= 20; i++ { allCards := rand.Perm(rule.POKER_CNT) var cardsArr [][]int32 for i := 0; i < 4; i++ { cardsArr = append(cardsArr, common.CopySliceIntToInt32(allCards[:13])) allCards = allCards[13:] } sort.Slice(cardsArr, func(i, j int) bool { cardsTypeMap := rule.GetCardsType(cardsArr[i], this.IsTienLenYule()) score, _ := rule.GetCardTypeScore(cardsTypeMap, this.IsTienLenYule(), cardsArr[i]) cardsTypeMap2 := rule.GetCardsType(cardsArr[j], this.IsTienLenYule()) score2, _ := rule.GetCardTypeScore(cardsTypeMap2, this.IsTienLenYule(), cardsArr[j]) return score > score2 }) if len(cardsArr) > 0 { cardsTypeMap := rule.GetCardsType(cardsArr[0], this.IsTienLenYule()) score, _ := rule.GetCardTypeScore(cardsTypeMap, this.IsTienLenYule(), cardsArr[0]) cardsTypeMap2 := rule.GetCardsType(cardsArr[len(cardsArr)-1], this.IsTienLenYule()) score2, _ := rule.GetCardTypeScore(cardsTypeMap2, this.IsTienLenYule(), cardsArr[len(cardsArr)-1]) if score-score2 > int(gameConfig.GetFenChaScore()) { logger.Logger.Tracef("分差发牌,分差:%v", score-score2) for _, v := range robotPlayers { if v == nil || !v.IsGameing() || v.cards[0] != rule.InvalideCard { continue } f1(v, cardsArr[0]) cardsArr = cardsArr[1:] } for _, v := range this.players { // map随机 if v == nil || !v.IsGameing() || v.cards[0] != rule.InvalideCard { continue } f1(v, cardsArr[len(cardsArr)-1]) cardsArr = cardsArr[:len(cardsArr)-1] v.TestLog = append(v.TestLog, fmt.Sprintf("分差发牌 snid%v 权重%v 需要分差%v 真实分差%v", v.SnId, gameConfig.GetFenCha(), gameConfig.GetFenChaScore(), score-score2)) } break } } } } } this.Shuffle(0, 0, 0) buf := this.poker.GetPokerBuf() cardsArr := make([][]int32, 4) for i, card := range buf { if int32(card) != rule.InvalideCard { index := i / 13 cardsArr[index] = append(cardsArr[index], int32(card)) } } f2 := func(players *[]*TienLenPlayerData) { if players == nil || len(*players) == 0 { return } var p *TienLenPlayerData // 发牌给这个玩家 var a int for _, v := range *players { a += int(math.Abs(float64(v.odds))) } n := this.RandInt(a) total := 0 for k, v := range *players { total += int(math.Abs(float64(v.odds))) if n < total { p = (*players)[k] *players = append((*players)[:k], (*players)[k+1:]...) break } } var cards []int32 if p.odds > 0 { // 拿好牌 cards = cardsArr[0] cardsArr = cardsArr[1:] } else { // 拿坏牌 cards = cardsArr[len(cardsArr)-1] cardsArr = cardsArr[:len(cardsArr)-1] } if p.cards[0] == rule.InvalideCard { f1(p, cards) } } if !isBad && !isTestPoker && !noviceCtrl && !noviceTianHu { // 天胡调控没有生效 if isGood { this.ctrlType = 1 // 牌平分,按从大到小排序 // 使用分差配置,最好牌和最差牌的牌型分差大于分差配置 for i := 0; i < 20; i++ { // 尝试20次,如果还不能满足分差配置,则直接发牌 cardsArr = cardsArr[:0] allCards := rand.Perm(rule.POKER_CNT) for i := 0; i < 4; i++ { cardsArr = append(cardsArr, common.CopySliceIntToInt32(allCards[:13])) allCards = allCards[13:] } sort.Slice(cardsArr, func(i, j int) bool { cardsTypeMap := rule.GetCardsType(cardsArr[i], this.IsTienLenYule()) score, _ := rule.GetCardTypeScore(cardsTypeMap, this.IsTienLenYule(), cardsArr[i]) cardsTypeMap2 := rule.GetCardsType(cardsArr[j], this.IsTienLenYule()) score2, _ := rule.GetCardTypeScore(cardsTypeMap2, this.IsTienLenYule(), cardsArr[j]) return score > score2 }) if len(cardsArr) > 0 { cardsTypeMap := rule.GetCardsType(cardsArr[0], this.IsTienLenYule()) score, _ := rule.GetCardTypeScore(cardsTypeMap, this.IsTienLenYule(), cardsArr[0]) cardsTypeMap2 := rule.GetCardsType(cardsArr[len(cardsArr)-1], this.IsTienLenYule()) score2, _ := rule.GetCardTypeScore(cardsTypeMap2, this.IsTienLenYule(), cardsArr[len(cardsArr)-1]) if score-score2 > int(gameConfig.GetGoodFenCha()) { break } } } // 排序 type gradeInfo struct { grade int cards []int32 } var grades []gradeInfo for _, card13 := range cardsArr { cardTmp := make([]int32, 13) copy(cardTmp, card13) cardsTypeMap := rule.GetCardsType(cardTmp, this.IsTienLenYule()) grade, _ := rule.GetCardTypeScore(cardsTypeMap, this.IsTienLenYule(), cardTmp) gi := gradeInfo{ grade: grade, cards: card13, } grades = append(grades, gi) } sort.Slice(grades, func(i, j int) bool { return grades[i].grade > grades[j].grade }) for i, gi := range grades { cardsArr[i] = gi.cards } // 发好牌 if isGood { l := len(realPlayersGood) for i := 0; i < l; i++ { f2(&realPlayersGood) } } // 发坏牌 if isBad { l := len(realPlayersBad) for i := 0; i < l; i++ { f2(&realPlayersBad) } } } } // 随机拿牌 for _, v := range this.players { // map随机 if v == nil || !v.IsGameing() || v.cards[0] != rule.InvalideCard { continue } f1(v, cardsArr[len(cardsArr)-1]) cardsArr = cardsArr[:len(cardsArr)-1] } // 测试 for _, player := range this.players { if player.IsGameing() && !player.IsRob { pack := &tienlen.SCTienLenCardTest{} pack.Totalout, pack.Totalin = player.TotalOutIn(int32(this.GetGameId())) if Grades != nil { pack.Grades = make(map[int32]int32) for id, grade := range Grades { pack.Grades[id] = int32(grade) } } logger.Logger.Trace("snid:", player.SnId) player.TestLog = append(player.TestLog, fmt.Sprintf("随机换牌 Good:%v G:%v Bad:%v B:%v", isGood, G, isBad, B)) pack.Data = strings.Join(player.TestLog, "\n") proto.SetDefaults(pack) player.SendToClient(int(tienlen.TienLenPacketID_PACKET_SCTienLenCardTest), pack) //logger.Logger.Trace("SnId: ", player.SnId, ";SCTienLenCardTest: ", pack) for _, v := range player.TestLog { logger.Logger.Trace(v) } } } } func (this *TienLenSceneData) SendHandCardTest() { this.poker.Shuffle() buf := this.poker.GetPokerBuf() //牌序- 2, A, K, Q, J, 10, 9, 8, 7, 6, 5, 4, 3 //红桃- 51,50,49,48,47,46,45,44,43,42,41,40,39 //方片- 38,37,36,35,34,33,32,31,30,29,28,27,26 //梅花- 25,24,23,22,21,20,19,18,17,16,15,14,13 //黑桃- 12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 test1 := []int32{35, 34, 33, 32, 31, 30, 0} test2 := []int32{42, 29, 16, 3} test3 := []int32{} test4 := []int32{} need1 := rule.HandCardNum - int32(len(test1)) need2 := rule.HandCardNum - int32(len(test2)) need3 := rule.HandCardNum - int32(len(test3)) need4 := rule.HandCardNum - int32(len(test4)) tmpBuf := []int32{} for i, card := range buf { for _, card1 := range test1 { if int32(card) == card1 { buf[i] = rule.Card(rule.InvalideCard) } } for _, card1 := range test2 { if int32(card) == card1 { buf[i] = rule.Card(rule.InvalideCard) } } for _, card1 := range test3 { if int32(card) == card1 { buf[i] = rule.Card(rule.InvalideCard) } } for _, card1 := range test4 { if int32(card) == card1 { buf[i] = rule.Card(rule.InvalideCard) } } } for _, card := range buf { if int32(card) != rule.InvalideCard { tmpBuf = append(tmpBuf, int32(card)) } } var n int minCard := int32(999) for _, seatPlayerEx := range this.seats { if seatPlayerEx != nil { bb := []int32{} for i := n * rule.Hand_CardNum; i < (n+1)*rule.Hand_CardNum; i++ { bb = append(bb, int32(buf[i])) } bb = []int32{} switch seatPlayerEx.GetPos() { case 0: for _, card := range test1 { bb = append(bb, card) } for i := int32(0); i < need1; i++ { bb = append(bb, tmpBuf[i]) } tmpBuf = append(tmpBuf[need1:]) case 1: for _, card := range test2 { bb = append(bb, card) } for i := int32(0); i < need2; i++ { bb = append(bb, tmpBuf[i]) } tmpBuf = append(tmpBuf[need2:]) case 2: for _, card := range test3 { bb = append(bb, card) } for i := int32(0); i < need3; i++ { bb = append(bb, tmpBuf[i]) } tmpBuf = append(tmpBuf[need3:]) case 3: for _, card := range test4 { bb = append(bb, card) } for i := int32(0); i < need4; i++ { bb = append(bb, tmpBuf[i]) } tmpBuf = append(tmpBuf[need4:]) } //排下序,正常应该客户端排序 sort.Slice(bb, func(i, j int) bool { v_i := rule.Value(int32(bb[i])) v_j := rule.Value(int32(bb[j])) c_i := rule.Color(int32(bb[i])) c_j := rule.Color(int32(bb[j])) if v_i > v_j { return false } else if v_i == v_j { return c_i < c_j } return true }) for idx, card := range bb { seatPlayerEx.cards[idx] = int32(card) if rule.Value(int32(card)) < rule.Value(minCard) { this.startOpPos = int32(seatPlayerEx.GetPos()) minCard = int32(card) this.curMinCard = minCard } else if rule.Value(int32(card)) == rule.Value(minCard) { if rule.Color(int32(card)) < rule.Color(minCard) { this.startOpPos = int32(seatPlayerEx.GetPos()) minCard = int32(card) this.curMinCard = minCard } } } pack := &tienlen.SCTienLenCard{} for j := int32(0); j < rule.HandCardNum; j++ { pack.Cards = append(pack.Cards, int32(seatPlayerEx.cards[j])) } pack.IsOutRecord = seatPlayerEx.CanUseRecordItem() proto.SetDefaults(pack) seatPlayerEx.SendToClient(int(tienlen.TienLenPacketID_PACKET_SCTienLenCard), pack) logger.Logger.Trace("player_id", seatPlayerEx.SnId, ";SCTienLenCard", pack.Cards) n++ } } } func (this *TienLenSceneData) SetCurOpPos(pos int32) { this.currOpPos = pos } func (this *TienLenSceneData) GetCurOpPos() int32 { return this.currOpPos } func (this *TienLenSceneData) GetCurOpPlayer() *TienLenPlayerData { if this.currOpPos < 0 || this.currOpPos >= int32(this.GetPlayerNum()) { return nil } return this.seats[this.currOpPos] } func (this *TienLenSceneData) SetLastOpPos(pos int32) { this.lastOpPos = pos //this.RefreshPlayerHandLimitTimeOut() } func (this *TienLenSceneData) GetLastOpPos() int32 { return this.lastOpPos } func (this *TienLenSceneData) GetLastOpPlayer() *TienLenPlayerData { if this.lastOpPos < 0 || this.lastOpPos >= int32(this.GetPlayerNum()) { return nil } return this.seats[this.lastOpPos] } func (this *TienLenSceneData) GetLastBombPlayer() *TienLenPlayerData { if this.lastBombPos < 0 || this.lastBombPos >= int32(this.GetPlayerNum()) { return nil } return this.seats[this.lastBombPos] } func (this *TienLenSceneData) GetCurBombPlayer() *TienLenPlayerData { if this.curBombPos < 0 || this.curBombPos >= int32(this.GetPlayerNum()) { return nil } return this.seats[this.curBombPos] } // 逆时针找一个空位 func (this *TienLenSceneData) FindOnePos() int { for i := 0; i < this.GetPlayerNum(); i++ { if this.seats[i] == nil { return i } } return int(rule.InvalidePos) } // 刷新玩家出牌时间 func (this *TienLenSceneData) RefreshPlayerHandLimitTimeOut() { CurPlayer := this.GetCurOpPlayer() if CurPlayer == nil { return } if CurPlayer.IsRobot() { return } //curCpCards := []int32{} //for _, card := range CurPlayer.cards { // if card != rule.InvalideCard { // curCpCards = append(curCpCards, card) // } //} lastOpPlayer := this.GetLastOpPlayer() if lastOpPlayer != nil && len(lastOpPlayer.delCards) != 0 { // lastDelCards := lastOpPlayer.delCards[len(lastOpPlayer.delCards)-1] //logger.Logger.Trace("(this *TienLenSceneData) RefreshPlayerHandLimitTimeOut lastOpPlayer snid: ", lastOpPlayer.SnId, " lastDelCards", lastDelCards) //recmCards := rule.RecommendCardsWithLastCards(lastDelCards, curCpCards) //if this.IsTienLenYule() { // recmCards = rule.RecommendCardsWithLastCards_yl(lastDelCards, curCpCards) //} //canDel, _, _ := rule.CanDel(lastDelCards, recmCards, this.IsTienLenToEnd()) //if this.IsTienLenYule() { // canDel, _, _ = rule.CanDel_yl(lastDelCards, recmCards, this.IsTienLenToEnd()) //} //logger.Logger.Trace("(this *TienLenSceneData) RefreshPlayerHandLimitTimeOut lastOpPlayer snid: ", lastOpPlayer.SnId, " ThinkLongCnt:", lastOpPlayer.ThinkLongCnt, " flag:", lastOpPlayer.GetFlag()) if int32(CurPlayer.GetPos()) != this.lastOpPos { //if !canDel { // 压不住别人 // //CurPlayer.curHandLimitTimeOut = rule.TienLenHandNotExceedTimeLimit // //CurPlayer.isNotOverLastHand = true // //logger.Logger.Trace("(this *TienLenSceneData) RefreshPlayerHandLimitTimeOut lastOpPlayer snid: ", lastOpPlayer.SnId, " CurPlayerSnid: ", CurPlayer.SnId, " ---压不住") //} else { // //CurPlayer.RefreshCurHandLimitTimeOut() // //CurPlayer.isNotOverLastHand = false // //logger.Logger.Trace("(this *TienLenSceneData) RefreshPlayerHandLimitTimeOut lastOpPlayer snid: ", lastOpPlayer.SnId, " CurPlayerSnid: ", CurPlayer.SnId, " ---可以压住") //} CurPlayer.RefreshCurHandLimitTimeOut() CurPlayer.isNotOverLastHand = false } else { CurPlayer.RefreshCurHandLimitTimeOut() CurPlayer.isNotOverLastHand = false //logger.Logger.Trace("(this *TienLenSceneData) RefreshPlayerHandLimitTimeOut lastOpPlayer snid: ", lastOpPlayer.SnId, " CurPlayerSnid: ", CurPlayer.SnId, " ---本轮自己首出牌") } //logger.Logger.Trace("(this *TienLenSceneData) RefreshPlayerHandLimitTimeOut lastOpPlayer lastOpPos: ", this.lastOpPos, " currOpPos: ", CurPlayer.GetPos()) } } func (this *TienLenSceneData) DoNext(pos int32) int32 { nextPos := this.GetNextOpPos(pos) if nextPos != rule.InvalidePos { this.SetCurOpPos(nextPos) this.StateStartTime = time.Now() } this.lastPos = pos //this.RefreshPlayerHandLimitTimeOut() //logger.Logger.Trace("(this *TienLenSceneData) DoNext pos: ", pos, " nextPos:", nextPos, " StateStartTime:", this.StateStartTime) this.cHintCards = []int32{} return nextPos } func (this *TienLenSceneData) GetNextOpPos(pos int32) int32 { if pos == rule.InvalidePos { return rule.InvalidePos } if pos < 0 || pos >= int32(this.GetPlayerNum()) { return rule.InvalidePos } for i := pos + 1; i < int32(this.GetPlayerNum()); i++ { if this.PlayerCanOp(i) { return i } } for i := int32(0); i < pos; i++ { if this.PlayerCanOp(i) { return i } } return rule.InvalidePos } func (this *TienLenSceneData) UnmarkPass() { for i := 0; i < this.GetPlayerNum(); i++ { if this.seats[i] != nil { this.seats[i].isPass = false } } } func (this *TienLenSceneData) FindWinPos() int { winPos := -1 if this.lastGamingPlayerNum != 0 && this.curGamingPlayerNum != 0 && this.lastWinSnid != 0 { haveLastWinPos := -1 for i := 0; i < this.GetPlayerNum(); i++ { if this.seats[i] != nil { if this.seats[i].SnId == this.lastWinSnid { haveLastWinPos = i break } } } if haveLastWinPos != -1 { if this.lastGamingPlayerNum > 2 { winPos = haveLastWinPos } else if this.lastGamingPlayerNum == 2 { if this.curGamingPlayerNum == 2 { winPos = haveLastWinPos } } } } return winPos } func (this *TienLenSceneData) TrySmallGameBilled() { // 看是不是炸弹,是炸弹结算分 if this.isKongBomb { this.bombToEnd++ this.isKongBomb = false } if this.roundScore > 0 && this.curBombPos != rule.InvalidePos && this.lastBombPos != rule.InvalidePos { winPlayer := this.GetCurBombPlayer() losePlayer := this.GetLastBombPlayer() baseScore := this.GetBaseScore() var rankScore = int64(this.roundScore) score := int64(this.roundScore) * int64(baseScore) if this.IsTienLenToEnd() { score = int64(this.roundScore) * int64(baseScore) / 100 //百分比 } losePlayerCoin := losePlayer.GetCoin() if !this.IsMatchScene() && !this.IsCustom() && losePlayerCoin < score { //输完 score = losePlayerCoin } //判断宠物技能生不生效 if losePlayer.PetUseSkill() { score = 0 //通知客户端宠物技能生效 炸弹不扣分 pack := &tienlen.SCTienLenPetSkillRes{} pack.Snid = losePlayer.SnId pack.Pos = int32(losePlayer.Pos) pack.PetSkillRes = true this.Broadcast(int(tienlen.TienLenPacketID_PACKET_SCTienLenPetSkillRes), pack, 0) logger.Logger.Trace("宠物技能抵挡炸弹生效,发送消息 SCTienLenPetSkillRes: ", pack) } if score != 0 { taxRate := this.GetDBGameFree().GetTaxRate() //万分比 gainScore := int64(float64(score) * float64(10000-taxRate) / 10000.0) //税后 bombTaxScore := score - gainScore // win if this.IsMatchScene() || this.IsCustom() { winPlayer.AddCoinNoLog(gainScore, 0) } else { winPlayer.AddCoin(gainScore, common.GainWay_CoinSceneWin, 0, "system", this.GetSceneName()) } winPlayer.winCoin += gainScore winPlayer.bombScore += gainScore winPlayer.bombTaxScore += bombTaxScore if this.IsTienLenToEnd() { winPlayer.bombRankScore += int64(float64(rankScore) / 100.0 * float64(rule.RankBaseScoreToEnd)) } else { winPlayer.bombRankScore += rankScore * rule.RankBaseScore } //lose if this.IsMatchScene() && this.IsCustom() { losePlayer.AddCoinNoLog(-score, 0) } else { losePlayer.AddCoin(-score, common.GainWay_CoinSceneLost, 0, "system", this.GetSceneName()) } losePlayer.winCoin -= score losePlayer.bombScore -= score if this.IsTienLenToEnd() { losePlayer.bombRankScore -= int64(float64(rankScore) / 100.0 * float64(rule.RankBaseScoreToEnd)) } else { losePlayer.bombRankScore -= rankScore * rule.RankBaseScore } pack := &tienlen.SCTienLenSmallGameBilled{ WinPos: proto.Int(winPlayer.GetPos()), WinPosCoin: proto.Int64(winPlayer.GetCoin()), WinCoin: proto.Int64(gainScore), LosePos: proto.Int(losePlayer.GetPos()), LosePosCoin: proto.Int64(losePlayer.GetCoin()), LoseCoin: proto.Int64(score), } proto.SetDefaults(pack) this.Broadcast(int(tienlen.TienLenPacketID_PACKET_SCTienLenSmallGameBilled), pack, 0) logger.Logger.Trace("SCTienLenSmallGameBilled: ", pack) } } this.curBombPos = rule.InvalidePos this.lastBombPos = rule.InvalidePos this.roundScore = 0 this.UnmarkPass() } func (this *TienLenSceneData) IsTianhuPlayer(snid int32) bool { for _, tianhusnid := range this.tianHuSnids { if snid == tianhusnid { return true } } return false } func (this *TienLenSceneData) IsWinPlayer(snid int32) bool { for _, winSnid := range this.winSnids { if snid == winSnid { return true } } return false } // SystemCoinOut 系统投入产出都要扣税 func (this *TienLenSceneData) SystemCoinOut() int64 { systemGain := int64(0) taxRate := this.GetDBGameFree().GetTaxRate() for i := 0; i < this.GetPlayerNum(); i++ { playerData := this.seats[i] if playerData != nil && playerData.IsGameing() && playerData.IsRob { gain := playerData.winCoin - playerData.bombScore if playerData.bombScore > 0 && gain > 0 { // 小结算赢,局结算都赢 systemGain += playerData.winCoin } else if playerData.bombScore > 0 && gain < 0 { // 小结算赢,局结算输 systemGain += playerData.bombScore - int64(float64(gain)*float64(10000-taxRate)/10000.0) } else if playerData.bombScore < 0 && gain > 0 { // 小结算输,局结算赢 systemGain += int64(float64(playerData.bombScore)*float64(10000-taxRate)/10000.0) + gain } else if playerData.bombScore < 0 && gain < 0 { // 小结算输,局结算输 systemGain += int64(float64(playerData.bombScore)*float64(10000-taxRate)/10000.0) + int64(float64(gain)*float64(10000-taxRate)/10000.0) } } } return systemGain } func (this *TienLenSceneData) SendFirstGiveTimeItem(p *base.Player) { if p.IsRobot() { return } if !p.PlayerData.IsTakeExpireItem { itemID := int32(60001) itemData := srvdata.GameItemMgr.Get(p.Platform, itemID) if itemData == nil { return } p.PlayerData.IsTakeExpireItem = true if p.ItemRecExpireTime <= 0 { p.ItemRecExpireTime = time.Now().Unix() + int64(itemData.Time)*3600*1 } else { if p.ItemRecExpireTime >= time.Now().Unix() { p.ItemRecExpireTime += int64(itemData.Time) * 3600 * 1 } else { p.ItemRecExpireTime = time.Now().Unix() + int64(itemData.Time)*3600*1 } } pack := &tienlen.SCTienLenPlayerFirstGiveItemItem{} pack.ItemId = itemID pack.ItemRecExpireTime = p.ItemRecExpireTime p.SendToClient(int(tienlen.TienLenPacketID_PACKET_SCTienLenFirstGiveItemItem), pack) } } // SaveCustomLog 保存竞技馆对局记录 func (this *TienLenSceneData) SaveCustomLog() { if this.CustomLogSave || !this.IsCustom() || this.NumOfGames == 0 { return } this.CustomLogSave = true state := int32(0) if len(this.RoundEndTime) < int(this.TotalOfGames) { state = 1 } log := &model.CustomLog{ Platform: this.Platform, CycleId: this.CycleID, RoomConfigId: this.GetCustom().GetRoomConfigId(), GameFreeId: this.GetGameFreeId(), TotalRound: this.TotalOfGames, PlayerNum: this.PlayerNum, Password: this.GetCustom().GetPassword(), CostType: this.GetCustom().GetCostType(), Voice: this.GetCustom().GetVoice(), RoomId: this.SceneId, StartTs: this.GameStartTime.Unix(), EndTs: time.Now().Unix(), State: state, } for snid := range this.BilledList { var items []*model.Item if this.PlayerAward[snid] != nil { items = *this.PlayerAward[snid] } log.SnId = append(log.SnId, model.PlayerInfo{ SnId: snid, Awards: items, }) } sort.Slice(log.SnId, func(i, j int) bool { p1 := base.PlayerMgrSington.GetPlayerBySnId(log.SnId[i].SnId) p2 := base.PlayerMgrSington.GetPlayerBySnId(log.SnId[j].SnId) return p1.GetCoin() > p2.GetCoin() }) for k, v := range this.RoundEndTime { score := make([]int64, len(this.BilledList)) for kk, vv := range log.SnId { if k < len(*this.BilledList[vv.SnId]) { score[kk] = (*this.BilledList[vv.SnId])[k].ChangeScore } } log.List = append(log.List, model.RoundInfo{ Round: int32(k + 1), Ts: v, Score: score, LogId: this.RoundLogId[k], }) } base.LogChannelSingleton.WriteLog(log) }