package chesstitians import ( "fmt" "math" "sort" "strings" "time" "mongo.games.com/goserver/core/logger" "mongo.games.com/game/common" rule "mongo.games.com/game/gamerule/chess" "mongo.games.com/game/gamesrv/base" "mongo.games.com/game/model" "mongo.games.com/game/proto" "mongo.games.com/game/protocol/chesstitians" "mongo.games.com/game/protocol/server" "mongo.games.com/game/srvdata" ) // SceneEx 房间上的额外数据 type SceneEx struct { *base.Scene // 场景 players map[int32]*PlayerEx // 玩家信息 seats [rule.MaxNumOfPlayer]*PlayerEx // 玩家 opPos int // 当前等待出牌的玩家位置 winSnId int32 // 赢家id masterSnId int32 // 房主 isAllRob bool // 是否是纯AI场 chess rule.Logic // 棋盘对象 lastMove []int32 // 上一步的行动 variant int // 棋类变种 对应fairy-stockfish stepSnId int32 // 计步玩家SnId stepNum int // 计步回合数 stepLimit int // 计步最大回合数 stepKing int // 孤王计步回合数 drawSnId int32 // 求和的玩家SnId nextSnId int32 // 再来一局玩家SnId } func getChessVariant(gameId int) int { var variant int switch gameId { case common.GameId_Chesstitians: variant = rule.ChessVarChess case common.GameId_ChesstitiansMakruk: variant = rule.ChessVarMakruk case common.GameId_ChesstitiansCambodian, common.GameId_ChesstitiansCambodianRobot: variant = rule.ChessVarCambodian } return variant } func NewSceneEx(s *base.Scene) *SceneEx { variant := getChessVariant(int(s.GameId)) chess := rule.NewChess(variant) chess.Init() sceneEx := &SceneEx{ Scene: s, chess: chess, players: make(map[int32]*PlayerEx), variant: variant, } return sceneEx } func (e *SceneEx) init() bool { // 初始配置 e.SetPlayerNum(rule.MaxNumOfPlayer) return true } func (e *SceneEx) Clear() { // 重置 e.chess.Init() e.opPos = rule.InvalidPos e.isAllRob = false e.winSnId = 0 e.nextSnId = 0 for _, player := range e.players { if player != nil { player.UnmarkFlag(base.PlayerState_WaitNext) } } e.lastMove = e.lastMove[:0] e.stepSnId = 0 e.stepNum = 0 e.stepLimit = 0 e.drawSnId = 0 } func (e *SceneEx) CanStart() bool { if e.GetGaming() { return false } if e.GetPlayerCnt() == rule.MaxNumOfPlayer { return true } return false } func (e *SceneEx) GetPlayerCnt() int { var cnt int for i := 0; i < e.GetPlayerNum(); i++ { playerEx := e.seats[i] if playerEx == nil { continue } cnt++ } return cnt } func (e *SceneEx) GetGamingPlayerCnt() int { var cnt int for i := 0; i < e.GetPlayerNum(); i++ { playerEx := e.seats[i] if playerEx == nil { continue } if playerEx.IsGameing() { cnt++ } } return cnt } func (e *SceneEx) GetRobotCnt() int { var cnt int for i := 0; i < e.GetPlayerNum(); i++ { playerEx := e.seats[i] if playerEx == nil { continue } if playerEx.IsRob { cnt++ } } return cnt } func (e *SceneEx) GetGamingRobotCnt() int { var cnt int for i := 0; i < e.GetPlayerNum(); i++ { playerEx := e.seats[i] if playerEx == nil { continue } if playerEx.IsRob && playerEx.IsGameing() { cnt++ } } return cnt } func (e *SceneEx) GetSeatPlayerCnt() int { var cnt int for i := 0; i < e.GetPlayerNum(); i++ { playerEx := e.seats[i] if playerEx == nil { continue } cnt++ } return cnt } func (e *SceneEx) delPlayer(p *base.Player) { if p, exist := e.players[p.SnId]; exist { e.seats[p.GetPos()] = nil delete(e.players, p.SnId) } } func (e *SceneEx) OnPlayerLeave(p *base.Player, reason int) { e.delPlayer(p) e.BroadcastPlayerLeave(p, reason) } func (e *SceneEx) SceneDestroy(force bool) { //销毁房间 e.Scene.Destroy(force) } func (e *SceneEx) BroadcastPlayerLeave(p *base.Player, reason int) { scLeavePack := &chesstitians.SCChesstitiansPlayerLeave{ Pos: proto.Int(p.GetPos()), } proto.SetDefaults(scLeavePack) logger.Logger.Trace("SCChesstitiansPlayerLeave: ", p.SnId, " scLeavePack: ", scLeavePack) e.Broadcast(int(chesstitians.ChesstitiansPacketID_PACKET_SCChesstitiansPlayerLeave), scLeavePack, p.GetSid()) } func (e *SceneEx) BroadcastAudienceNum(p *base.Player) { pack := &chesstitians.SCChesstitiansUpdateAudienceNum{ AudienceNum: proto.Int(e.GetAudiencesNum()), } proto.SetDefaults(pack) e.Broadcast(int(chesstitians.ChesstitiansPacketID_PACKET_SCChesstitiansUpdateAudienceNum), pack, p.GetSid()) } // BroadcastUpdateMasterSnId 广播房主更换 func (e *SceneEx) BroadcastUpdateMasterSnId(changeState bool) { pack := &chesstitians.SCChesstitiansUpdateMasterSnid{ MasterSnid: proto.Int32(e.masterSnId), } proto.SetDefaults(pack) e.Broadcast(int(chesstitians.ChesstitiansPacketID_PACKET_SCChesstitiansUpdateMasterSnid), pack, 0) //logger.Logger.Trace("BroadcastUpdateMasterSnId ", pack) //重置开始倒计时 if changeState && e.CanStart() { e.Scene.ChangeSceneState(rule.SceneStateWaitStart) } } func (e *SceneEx) playerOpPack(snId int32, opcode int, opRetCode chesstitians.OpResultCode, params []int64) *chesstitians.SCChesstitiansPlayerOp { pack := &chesstitians.SCChesstitiansPlayerOp{ OpCode: proto.Int(opcode), OpParam: params, SnId: proto.Int32(snId), OpRetCode: opRetCode, Check: e.chess.GetCheck(), Checkmate: e.chess.GetCheckmate(), StepSnId: e.stepSnId, StepNum: int64(proto.Int(e.stepNum)), StepLimit: int64(e.stepLimit), } p := e.GetPlayer(snId) if p != nil { if ex, ok := p.ExtraData.(*PlayerEx); ok { pack.TotalTime = append(pack.TotalTime, ex.GetTotalTime()) for _, v := range e.seats { if v != nil && v.Pos != ex.Pos { pack.TotalTime = append(pack.TotalTime, v.GetTotalTime()) break } } } } proto.SetDefaults(pack) return pack } // OnPlayerSCOp 发送玩家操作情况 func (e *SceneEx) OnPlayerSCOp(p *base.Player, opcode int, opRetCode chesstitians.OpResultCode, params []int64) { pack := e.playerOpPack(p.SnId, opcode, opRetCode, params) p.SendToClient(int(chesstitians.ChesstitiansPacketID_PACKET_SCChesstitiansPlayerOp), pack) logger.Logger.Tracef("OnPlayerSCOp %s", pack) } // BroadcastPlayerSCOp 广播发送玩家操作情况 func (e *SceneEx) BroadcastPlayerSCOp(snid int32, opcode int, opRetCode chesstitians.OpResultCode, params []int64) { pack := e.playerOpPack(snid, opcode, opRetCode, params) e.Broadcast(int(chesstitians.ChesstitiansPacketID_PACKET_SCChesstitiansPlayerOp), pack, 0) logger.Logger.Tracef("BroadcastPlayerSCOp %s", pack) } // FindOnePos 逆时针找一个空位 func (e *SceneEx) FindOnePos() int { for i := 0; i < e.GetPlayerNum(); i++ { if e.seats[i] == nil { return i } } return rule.InvalidPos } func (e *SceneEx) SystemCoinOut() int64 { systemGain := int64(0) for i := 0; i < e.GetPlayerNum(); i++ { playerData := e.seats[i] if playerData != nil && playerData.IsGameing() && playerData.IsRob { systemGain += playerData.winCoin } } return systemGain } func (e *SceneEx) AllPlayerEnterGame() { for i := 0; i < e.GetPlayerNum(); i++ { if e.seats[i] == nil { continue } if e.seats[i].IsMarkFlag(base.PlayerState_GameBreak) == false { e.seats[i].UnmarkFlag(base.PlayerState_WaitNext) e.seats[i].SyncFlag() } } } func (e *SceneEx) SetCurOpPos(pos int) { e.opPos = pos e.seats[pos].lastTime = time.Now() } func (e *SceneEx) GetCurOpPos() int { return e.opPos } func (e *SceneEx) AddChessGrade(p *PlayerEx, num int64) { if p == nil { return } // 人机对战没有积分 if e.GetGameId() != common.GameId_ChesstitiansCambodianRobot { p.AddChessGrade(num) } } func (e *SceneEx) AddCoin(p *PlayerEx, num int64, gainWay int32, syncFlag int, oper, remark string) { if p == nil { return } // 人机对战没有金币结算 if e.GetGameId() != common.GameId_ChesstitiansCambodianRobot { p.AddCoin(num, gainWay, syncFlag, oper, remark) } } func (e *SceneEx) GetSeatByCKey(cKey string) *PlayerEx { _cKey := strings.ToLower(cKey) for _, seat := range e.seats { if _cKey == "w" && !seat.isBlack || _cKey == "b" && seat.isBlack { return seat } } return nil } // ChessInit 棋盘初始化 func (e *SceneEx) ChessInit() { e.chess.Init() e.lastMove = []int32{} // 空结构 // 随机 firstIdx := e.RandInt(2) fmt.Println("@@firstIdx", firstIdx) for idx, seat := range e.seats { if seat != nil { if idx == firstIdx { // 默认seats中的第一个是白棋,先走 seat.isBlack = false } else { seat.isBlack = true } seat.oldGrade = seat.ChessGrade pack := &chesstitians.SCChesstitiansCard{} // 通过cards[0] 传递isBlack if seat.isBlack { pack.Cards = append(pack.Cards, int32(1)) } else { pack.Cards = append(pack.Cards, int32(0)) } // 生成棋数据 pack.Chess = e.chess.GetChess() pack.Act = e.chess.GetAct() pack.Castling = e.chess.GetCastling() pack.CambodianMove = e.chess.GetCambodianMove() pack.Enpassant = int32(e.chess.GetEnPassant()) pack.ChessRound = int32(e.chess.GetRound()) pack.Check = e.chess.GetCheck() pack.Checkmate = e.chess.GetCheckmate() proto.SetDefaults(pack) seat.SendToClient(int(chesstitians.ChesstitiansPacketID_PACKET_SCChesstitiansCard), pack) } } } // PlayChess 判断客户端发来的行棋操作,返回是否合法 func (e *SceneEx) PlayChess(fromTo []int64) bool { fromIdx, toIdx := int(fromTo[0]), int(fromTo[1]) // 判断该谁走 // 检查棋子本身能不能走 if !e.chess.CanMove(fromIdx, toIdx) { logger.Logger.Errorf("非法行棋(棋子本身的路线错误) %v %v %v", fromIdx, toIdx, e.chess.GetPiece(fromIdx)) return false } // 王被吃 toPiece := e.chess.GetPiece(toIdx) if toPiece == rule.WK { // 白王被吃,标记黑方获胜 for _, seat := range e.seats { if seat != nil { if seat.isBlack { e.winSnId = seat.GetSnId() } } } } else if toPiece == rule.BK { // 黑王被吃,标记白方获胜 for _, seat := range e.seats { if !seat.isBlack { e.winSnId = seat.GetSnId() } } } c := e.chess.GetPiece(fromIdx) e.chess.Move(fromIdx, toIdx) e.lastMove = []int32{int32(fromIdx), int32(toIdx)} // 将死 if rule.IsWhite(c) && e.chess.IsCheckmate(true) { // 白方胜 e.winSnId = e.GetSeatByCKey("w").GetSnId() } else if !rule.IsWhite(c) && e.chess.IsCheckmate(false) { // 黑方胜 e.winSnId = e.GetSeatByCKey("b").GetSnId() } // 无子可动平局 if rule.IsWhite(c) && e.chess.IsStop(false) { e.ChangeSceneState(rule.SceneStateBilled) } else if rule.IsBlack(c) && e.chess.IsStop(true) { e.ChangeSceneState(rule.SceneStateBilled) } return true } // NextOpPos 轮换到下一个操作的玩家 func (e *SceneEx) NextOpPos() { lastPlayer := e.seats[e.opPos] if !lastPlayer.lastTime.IsZero() { lastPlayer.totalTime += int64(time.Now().Sub(lastPlayer.lastTime).Seconds()) } e.opPos = (e.opPos + 1) % rule.MaxNumOfPlayer e.chess.NextAct() e.chess.PrintChess() s := e.chess.GenFen() println(s) // 计步 if e.IsStep() { curPlayer := e.seats[e.opPos] if curPlayer.SnId == e.stepSnId { e.stepNum++ } if e.IsKingStep() { e.stepKing++ if e.stepKing == 2 { e.stepNum++ e.stepKing = 0 } } if e.stepNum > e.stepLimit { // 计步结束,平局 e.ChangeSceneState(rule.SceneStateBilled) return } } // 检查孤王计步 // 任何一方只剩一个王时,开始计步;取消残局计步;确定回合限制 if !e.IsKingStep() { if e.chess.Has(rule.WK) && e.chess.GetWhiteNum() == 1 || e.chess.Has(rule.BK) && e.chess.GetBlackNum() == 1 { if e.IsStep() { e.cancelStep() } e.StartKingStep() } } // 记录操作开始时间 e.seats[e.opPos].lastTime = time.Now() } // IsStep 是否计步中 func (e *SceneEx) IsStep() bool { if e.stepLimit > 0 { return true } return false } func (e *SceneEx) IsKingStep() bool { if e.stepSnId == 0 && e.stepLimit > 0 { return true } return false } // CanStep 是否可以计步 func (e *SceneEx) CanStep() bool { if e.IsStep() { return false } // 双方都没有兵 if e.chess.GetChessNum(rule.WP) == 0 && e.chess.GetChessNum(rule.BP) == 0 { return true } return false } // StartStep 玩家开始计步 func (e *SceneEx) StartStep(p *PlayerEx) bool { if !e.CanStep() { return false } e.stepSnId = p.GetSnId() e.stepNum = 1 e.stepLimit = rule.PlayerStepLimit return true } // StartKingStep 开始孤王计步 func (e *SceneEx) StartKingStep() { e.stepKing = 0 c := rule.White if e.chess.Has(rule.WK) && e.chess.GetWhiteNum() == 1 { c = rule.Black } //双车:8回合 //单车:16回合 //双象:22回合 //双马:32回合 //单象:44回合 //单马:64回合 //其他情况:64回合 r := e.chess.GetChessNum(rule.ToPiece(c, rule.R)) if r == 2 { e.stepLimit = 8 } else if r == 1 { e.stepLimit = 16 } if e.stepLimit == 0 { s := e.chess.GetChessNum(rule.ToPiece(c, rule.S)) if s == 2 { e.stepLimit = 22 } else { n := e.chess.GetChessNum(rule.ToPiece(c, rule.N)) if n == 2 { e.stepLimit = 32 } else if s == 1 { e.stepLimit = 44 } else if n == 1 { e.stepLimit = 64 } } } if e.stepLimit == 0 { e.stepLimit = 64 } // 起始回合数 //e.stepNum = e.chess.GetWhiteNum() + e.chess.GetBlackNum() + 1 e.stepNum = 1 } // cancelStep 取消计步 func (e *SceneEx) cancelStep() { e.stepSnId = 0 e.stepNum = 0 e.stepLimit = 0 } // CancelStep 玩家取消计步 func (e *SceneEx) CancelStep(p *PlayerEx) bool { if !e.IsStep() { return false } if e.stepSnId != p.GetSnId() { return false } e.cancelStep() return true } // RequestDraw 玩家求和 func (e *SceneEx) RequestDraw(p *PlayerEx) bool { if e.drawSnId > 0 { return false } e.drawSnId = p.GetSnId() return true } // RefuseDraw 玩家拒绝求和 func (e *SceneEx) RefuseDraw(p *PlayerEx) bool { if e.drawSnId == 0 { return false } if e.drawSnId == p.GetSnId() { return false } e.drawSnId = 0 return true } // AgreeDraw 玩家同意求和 func (e *SceneEx) AgreeDraw(p *PlayerEx) bool { if e.drawSnId == 0 { return false } if e.drawSnId == p.GetSnId() { return false } e.drawSnId = 0 e.ChangeSceneState(rule.SceneStateBilled) return true } // CancelDraw 撤销求和 func (e *SceneEx) CancelDraw(p *PlayerEx) bool { if e.drawSnId == 0 { return false } if e.drawSnId != p.GetSnId() { return false } e.drawSnId = 0 return true } func (e *SceneEx) GetChessRank(p *PlayerEx) int32 { i := sort.Search(len(e.ChessRank), func(i int) bool { return int64(e.ChessRank[i]) > p.ChessGrade }) if i == len(e.ChessRank) { return e.ChessRank[i-1] } i = i - 1 if i < 0 { i = 0 } if i < len(e.ChessRank) { return e.ChessRank[i] } return 0 } func (e *SceneEx) GetChessRankNext(p *PlayerEx) int32 { i := sort.Search(len(e.ChessRank), func(i int) bool { return int64(e.ChessRank[i]) > p.ChessGrade }) if i == len(e.ChessRank) { return e.ChessRank[i-1] } return e.ChessRank[i] } func (e *SceneEx) ToBilled(p1, p2 *PlayerEx, isWin int) (*chesstitians.ChesstitiansPlayerGameBilled, *model.ChesstitiansPerson) { // 获取结算规则 var typeId int32 var billedRule, billedRuleDraw *server.DB_ChessBilledRules // 人机对战不结算 if e.GetGameId() != common.GameId_ChesstitiansCambodianRobot { winRank := e.GetChessRank(p1) loseRank := e.GetChessRank(p2) if winRank > loseRank { typeId = rule.BilledTypeGT } else if winRank < loseRank { typeId = rule.BilledTypeLT } else { typeId = rule.BilledTypeEQ } for _, v := range srvdata.PBDB_ChessBilledRulesMgr.Datas.Arr { if v.GetTypeId() == typeId { billedRule = v } if v.GetTypeId() == rule.BilledTypeEQ { billedRuleDraw = v } } } p1.isWin = int32(isWin) p1.winCoin = 0 p1.taxCoin = 0 p1.winTimes = 0 p1.winScore = 0 p1.otherScore = 0 p1.nextRank = 0 switch isWin { case rule.Win: // 金币和税收 gainScore := int64(float64(e.GetBaseScore()) * float64(10000-e.GetDBGameFree().GetTaxRate()) / 10000.0) p1.winCoin = gainScore p1.taxCoin = int64(e.GetBaseScore()) - gainScore // 连赢次数 winTimes := p1.GetGameFreeIdData(e.KeyGamefreeId).Statics.WinGameTimesNum e.AddCoin(p1, gainScore, common.GainWay_CoinSceneWin, 0, "system", e.GetSceneName()) p1.winTimes = int32(winTimes + 1) var winScore int32 if billedRule != nil { // 连赢积分 if p1.GetGameFreeIdData(e.KeyGamefreeId).Statics.GetInt(rule.StaticsChessTime)+1 >= int(billedRule.GetWinTimes()) { p1.GetGameFreeIdData(e.KeyGamefreeId).Statics.SetInt(rule.StaticsChessTime, 0) p1.AddChessGrade(int64(billedRule.OtherScore)) p1.otherScore = billedRule.OtherScore winScore += billedRule.OtherScore } else { p1.GetGameFreeIdData(e.KeyGamefreeId).Statics.Incr(rule.StaticsChessTime) } // 总积分 e.AddChessGrade(p1, int64(billedRule.GetWinScore())) // 本局积分 winScore += billedRule.GetWinScore() p1.winScore = winScore } case rule.Lose: e.AddCoin(p1, int64(-e.GetDBGameFree().GetBaseScore()), common.GainWay_CoinSceneLost, 0, "system", e.GetSceneName()) p1.GetGameFreeIdData(e.KeyGamefreeId).Statics.SetInt(rule.StaticsChessTime, 0) p1.winCoin = int64(-e.GetDBGameFree().GetBaseScore()) if billedRule != nil { // 总积分 e.AddChessGrade(p1, int64(billedRule.GetLoseScore())) // 本局积分 p1.winScore = proto.Int32(billedRule.GetLoseScore()) } case rule.Draw: p1.GetGameFreeIdData(e.KeyGamefreeId).Statics.SetInt(rule.StaticsChessTime, 0) if billedRule != nil { // 总积分 e.AddChessGrade(p1, int64(billedRule.GetDrawScore())) // 本局积分 p1.winScore = proto.Int32(billedRule.GetDrawScore()) } } if billedRuleDraw != nil { // 还有几局晋级 n := math.Ceil(float64(int64(e.GetChessRankNext(p1))-p1.ChessGrade) / float64(billedRuleDraw.GetWinScore())) p1.nextRank = int32(n) } // 人机对战,没有输赢分,没有积分变化 if e.GetGameId() == common.GameId_ChesstitiansCambodianRobot { p1.winCoin = 0 p1.taxCoin = 0 p1.winScore = 0 p1.otherScore = 0 } billData := &chesstitians.ChesstitiansPlayerGameBilled{ SnId: proto.Int32(p1.SnId), WinCoin: proto.Int64(p1.winCoin), GameCoin: proto.Int64(p1.GetCoin()), IsWin: proto.Int32(p1.isWin), WinTimes: proto.Int32(p1.winTimes), OtherScore: proto.Int32(p1.otherScore), NextRank: proto.Int32(p1.nextRank), Score: proto.Int32(int32(p1.ChessGrade)), WinScore: proto.Int32(p1.winScore), OldChessGrade: proto.Int64(p1.oldGrade), } chessPerson := &model.ChesstitiansPerson{ UserId: p1.SnId, UserIcon: p1.Head, Platform: p1.Platform, Channel: p1.Channel, Promoter: p1.BeUnderAgentCode, PackageTag: p1.PackageID, InviterId: p1.InviterId, WBLevel: p1.WBLevel, IsRob: p1.IsRob, IsFirst: e.IsPlayerFirst(e.GetPlayer(p1.SnId)), IsLeave: false, IsWin: p1.isWin, Seat: p1.GetPos(), GainCoin: p1.winCoin, GainTaxCoin: p1.taxCoin, } return billData, chessPerson }