package gamelogic import ( "encoding/json" "fmt" "bet24.com/log" task "bet24.com/servers/micros/task/proto" ) // 公共数据定义 const ( GAMEID = 37 GAME_NAME = "baloot" CHAIR_COUNT = 4 SCORE_TO_WIN = 152 LAST_WIN_SCORE = 10 ALL_WIN_EXTRA_SCORE = 90 HOKUM_TOTAL_SCORE = 152 SUN_TOTAL_SCORE = 120 MIN_OUT_MS = 500 VALUE_DOUBLING_LEN = 15 WINDUP_SCORE = 120 MAX_DOUBLING_LIMIT = 256 ) // 任务类型定义 const ( TaskAction_Baloot80 = task.TaskAction_gameaction + 1 TaskAction_Baloot100 = task.TaskAction_gameaction + 2 TaskAction_Baloot120 = task.TaskAction_gameaction + 3 TaskAction_Baloot_Sun = task.TaskAction_gameaction + 4 ) // 机器人默认动作时间 const ( MS_MinRobotNotFirstOut = 800 // 非首出最小时间(毫秒) MS_MaxRobotNotFirstOut = 1000 // 非首出最大时间(毫秒) MS_DiminishingNotFirstOut = 50 // 非首出递减时间(毫秒) MS_MinRobotFirstOut = 1200 // 首出最小时间(毫秒) MS_MaxRobotFirstOut = 1500 // 首出最大时间(毫秒) MS_DiminishingFirstOut = 100 // 首出递减时间(毫秒) ) // 上报分数类型 const ( ScoreType_Bet = 0 ScoreType_End = 1 ScoreType_Return = 2 ScoreType_Flee = 3 scoreType_Surrender = 4 ) const ( RobotChatAction_MuiltyTimeOut = iota RobotChatAction_LoseOverFive RobotChatAction_LoseOverEighty RobotChatAction_FriendGetBigProject RobotChatAction_SelfWinOverFive RobotChatAction_GoodHandCard RobotChatAction_BadHandCard RobotChatAction_EmenyGetBigProject RobotChatAction_EnterRoom RobotChatAction_Reply RobotChatAction_FriendWinOverFive ) // commands const ( CMD_ROOMINFO = "CMD_ROOMINFO" CMD_TABLECHAT = "CMD_TABLECHAT" CMD_CANCLE_AUTO = "CMD_CANCLE_AUTO" CMD_AUTO = "CMD_AUTO" CMD_ACTION = "CMD_ACTION" CMD_ACTION_FAILED = "CMD_ACTION_FAILED" CMD_SHOW_CARDTYPE = "CMD_SHOW_CARDTYPE" CMD_CALL_BALOOT = "CMD_CALL_BALOOT" CMD_SHOWDOUBLING = "CMD_SHOWDOUBLING" CMD_SYNCDATA = "CMD_SYNCDATA" CMD_SURRENDER = "CMD_SURRENDER" CMD_SURRENDER_RESULT = "CMD_SURRENDER_RESULT" CMD_LADDERINFO = "CMD_LADDERINFO" CMD_SMALL_RESULT = "CMD_SMALL_RESULT" ) // 游戏阶段 const ( Phase_Free = iota Phase_Start // 发牌 Phase_FirstBuy // 第一次买牌阶段 Phase_SecondBuy // 第二次买牌阶段 Phase_ChooseTrump // 选择花色 Phase_Reshuffle // 是否重新洗牌 Phase_Double // 加倍阶段 Phase_CloseOpen // 开关阶段 Phase_SendLeftCard // 发剩余牌阶段 Phase_Play // 出牌 Phase_GameEnd // 中间结算 Phase_End // 结束 Phase_Correct // 纠错模式 ) // 游戏阶段描述 func getPhaseDesc(phase int) string { switch phase { case Phase_Free: return "Free" case Phase_Start: return "Start" case Phase_FirstBuy: return "FirstBuy" case Phase_SecondBuy: return "SecondBuy" case Phase_ChooseTrump: return "ChooseTrump" case Phase_Reshuffle: return "Reshuffle" case Phase_Double: return "Double" case Phase_CloseOpen: return "CloseOpen" case Phase_SendLeftCard: return "SendLeftCard" case Phase_Play: return "Play" case Phase_Correct: return "Correct" case Phase_GameEnd: return "GameEnd" case Phase_End: return "End" } return "Invalid" } // 定时器ID const ( TIMER_READY_0 = iota TIMER_READY_1 TIMER_READY_2 TIMER_READY_3 TIMER_GAME TIMER_REMOVE_ROBOT TIMER_ADD_ROBOT TIMER_SEND_LEFT_CARD TIMER_START_OUT_CARD TIMER_NEWROUND TIMER_ROBOT_ACTION_0 TIMER_ROBOT_ACTION_1 TIMER_ROBOT_ACTION_2 TIMER_ROBOT_ACTION_3 TIMER_AUTO_ACTION_0 TIMER_AUTO_ACTION_1 TIMER_AUTO_ACTION_2 TIMER_AUTO_ACTION_3 TIMER_ALL_PASS // 所有玩家两次都不买牌,延迟重新发牌 TIMER_BUY_CHANGE TIMER_DOUBLE_CHANGE TIMER_DELAY_END TIMER_START_GAME TIMER_SAWA TIMER_CORRECTFINISH TIMER_CORRECTDELAY TIMER_SURRENDER TIMER_ROBOT_SURRENDER TIMER_ROBOT_REPLY TIMER_ROBOT_SEND_CHAT_0 TIMER_ROBOT_SEND_CHAT_1 TIMER_ROBOT_SEND_CHAT_2 TIMER_ROBOT_SEND_CHAT_3 ) // 定时器时间 const ( SEC_READY = 60 * 1000 //准备计时 SEC_START = 5000 SEC_AUTO = 800 SEC_SET_END = 3 * 1000 SEC_ROBOT_CHAT = 7000 SEC_SEND_LEFT_CARD = 1000 SEC_START_OUT_CARD = 2000 SEC_NEWROUND = 1000 SEC_ALL_PASS = 100 SEC_RESHUFFLE = 10 * 1000 SEC_SAWA = 5000 SEC_CORRECTFINISH = 5000 SCE_CORRECT_DELAY = 3000 ) // 动作类型 const ( Action_Buy = iota // 买牌 Action_ChooseTrump // 选择花色 Action_Reshuffle // 重洗 Action_Double // hokum模式下加倍操作 Action_CloseOrOpen // 是否打开和关闭 Action_OutCard // 出一张牌 Action_Gawah // 把手中牌全部打出去(在玩家手中牌全部为最大的时候) Action_GameEnd Action_RoundEnd Action_RoundStart Action_CorrectionStart Action_CorrectionFinish Action_Sawa Action_Invalid ) // 动作描述 func getActionDesc(action int) string { switch action { case Action_Buy: return "buy" case Action_Double: return "dbl" case Action_Reshuffle: return "shuffle" case Action_ChooseTrump: return "cho" case Action_CloseOrOpen: return "clorop" case Action_Gawah: return "gawah" case Action_OutCard: return "out" case Action_CorrectionStart: return "correctStart" case Action_CorrectionFinish: return "correctFinish" case Action_Sawa: return "sawa" case Action_GameEnd: return "---game_end---" case Action_RoundEnd: return "--" } return "n/a" } // 买牌的操作Param const ( Action_Buy_Pass = iota Action_Buy_Hokum Action_Buy_Sun Action_Buy_Ashkal Action_Buy_ConfirmHokum Action_Buy_SwitchSun Action_Buy_Double Action_Buy_Triple Action_Buy_Four Action_Buy_Coffee Action_Buy_Invaild ) // 买牌的操作描述 func getBuyActionDesc(action int) string { switch action { case Action_Buy_Pass: return "Pass" case Action_Buy_Hokum: return "Hokum" case Action_Buy_Sun: return "Sun" case Action_Buy_Ashkal: return "Ashkal" case Action_Buy_ConfirmHokum: return "ConfirmHokum" case Action_Buy_SwitchSun: return "SwitchSun" case Action_Buy_Double: return "Double" case Action_Buy_Triple: return "Triple" case Action_Buy_Four: return "Four" case Action_Buy_Coffee: return "Coffee" } return "Invalid" } // 游戏模式定义 const ( Suit_Hokum = iota // 有主 Suit_Sun // 无主 Suit_Ashkal // 无主公共牌给对友 Suit_Invalid ) // 游戏模式描述 func getSuitDesc(suit int) string { switch suit { case Suit_Hokum: return "Hokum" case Suit_Sun: return "Sun" case Suit_Ashkal: return "Ashkal" } return "Invalid" } // 开关动作定义 const ( Action_Buy_Close = 1 Action_Buy_Open = 2 ) // 开关动作描述 func getCloseOrOpenDesc(data int) string { switch data { case Action_Buy_Close: return "close" case Action_Buy_Open: return "open" } return "Invalid" } // 重洗动作 const ( Reshuffle_None = iota Reshuffle_Pass // pass不重洗 Reshuffle_Confirm // 确定重洗 ) // 重洗描述 func getReshuffleDesc(action int) string { switch action { case Reshuffle_Pass: return "Pass" case Reshuffle_Confirm: return "Confirm" } return "Invalid" } // project 类型 const ( PROJECT_SIRA = iota PROJECT_FIFTY PROJECT_HUNDRED PROJECT_FOURHUNDRED PROJECT_BALOOT PROJECT_INVALID ) var value_project = []int{20, 50, 100, 200, 20} var value_project_task = []int{20, 50, 100, 400, 20} // 项目数据定义 type SingleProject struct { Type int Score int Cards []int taskScore int // 任务分 } // 项目比较 func (sp SingleProject) isBiggerThan(project SingleProject) bool { if sp.Type > project.Type { return true } if sp.Type < project.Type { return false } if getCardValue(sp.Cards[0]) > getCardValue(project.Cards[0]) { return true } if getCardValue(sp.Cards[0]) < getCardValue(project.Cards[0]) { return false } return getCardType(sp.Cards[0]) > getCardType(project.Cards[0]) } // 项目描述 func (sp SingleProject) getProjectHex() string { str := getCardsHex(sp.Cards) switch sp.Type { case PROJECT_SIRA: return "sira:" + str case PROJECT_FIFTY: return "50:" + str case PROJECT_HUNDRED: return "100:" + str case PROJECT_FOURHUNDRED: return "400:" + str case PROJECT_BALOOT: return "baloot:" + str } return "" } // 买牌和出牌的动作 type CmdAction struct { Action int Param int Projects []int CorrectCards []int } // 玩家动作失败 type CmdActionFailed struct { CmdAction ErrMsg string } // 纠错模式定义 const ( Correct_Normal = iota Correct_Sawa Correct_Invaild ) // 纠错模式描述 func getCorrectModeDesc(mode int) string { switch mode { case Correct_Normal: return "Normal" case Correct_Sawa: return "Sawa" } return "Invaild" } // 纠错类型定义 const ( CorrectType_SameType = iota // 手上有相同花色没有出 CorrectType_BigCardInHokum // 在hokum模式下,这轮花色为主牌花色主牌,玩家手上有必须要出的BigCard却没出 CorrectType_Close // 关闭状态下,第一个出牌的人出了主牌花色,但是其手上还有其他花色 CorrectType_TrumpCard // 在hokum模式下,这轮花色为非主牌花色主牌,玩家手上有必须要出的BigCard却没出 CorrectType_Invaild ) func getCorrectTypeDesc(correctType int) string { switch correctType { case CorrectType_SameType: return "SameType" case CorrectType_BigCardInHokum: return "BigCardInHokum" case CorrectType_Close: return "Close" case CorrectType_TrumpCard: return "TrumpCard" } return "InvaildType" } // 翻倍类型定义 const ( Doubling_Project_Sira = iota Doubling_Project_Fifty Doubling_Project_Hundred Doubling_Project_FourHundred Doubling_Project_Baloot Doubling_PublicCard_J Doubling_Call_Double Doubling_Call_Triple Doubling_Call_Four Doubling_Call_Coffee Doubling_Hokum_to_Sun Doubling_Hokum_to_Ashkal Doubling_Ashkal_to_Sun Doubling_Sun_to_Sun Doubling_ConsecutiveWinOverFour ) // 翻倍类型描述 func getDoublingTypeHex(doublingType int) string { switch doublingType { case Doubling_Project_Sira: return "Project_Sira" case Doubling_Project_Fifty: return "Project_Fifty" case Doubling_Project_Hundred: return "Project_Hundred" case Doubling_Project_FourHundred: return "Project_FourHundred" case Doubling_Project_Baloot: return "Project_Baloot" case Doubling_PublicCard_J: return "PublicCard_J" case Doubling_Call_Double: return "Call_Double" case Doubling_Call_Triple: return "Call_Triple" case Doubling_Call_Four: return "Call_Four" case Doubling_Call_Coffee: return "Call_Coffee" case Doubling_Hokum_to_Sun: return "Doubling_Hokum_to_Sun" case Doubling_Hokum_to_Ashkal: return "Doubling_Hokum_to_Ashkal" case Doubling_Ashkal_to_Sun: return "Doubling_Ashkal_to_Sun" case Doubling_Sun_to_Sun: return "Doubling_Sun_to_Sun" case Doubling_ConsecutiveWinOverFour: return "ConsecutiveWinOverFour" } return "Invaild DoublingType" } var value_doubling = []int{2, 3, 4, 8, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2} // 翻倍数据 type DoublingData struct { DoublingType int DoublingTimes int } // 翻倍数据描述 func (dd *DoublingData) dump() { log.Debug(" type[%s] times:%d", getDoublingTypeHex(dd.DoublingType), dd.DoublingTimes) } // 每轮赢牌方翻倍总分和对应轮数 type DoublingDetail struct { RoundIndex int DoublingTotalTimes int } // 每轮赢牌方翻倍总分和对应轮数描述 func (dd *DoublingDetail) dump() { log.Debug(" roundIndex:%d totalTimes:%d", dd.RoundIndex, dd.DoublingTotalTimes) } // CMD_SHOWDOUBLING数据结构 type ShowDoubling struct { Index int Times int } // CMD_SHOW_CARDTYPE数据结构 type ShowCardType struct { Type int // 展示的花色 Chair int // 谁买的花色 } // 上报回放数据结构 type userAction struct { ChairId int Action int Data int ExtData []int } func isAllZero(data []int) bool { for i := 0; i < len(data); i++ { if data[i] > 0 { return false } } return true } // 上报回放数据描述 func (ua *userAction) dump() { data := fmt.Sprintf("%d", ua.Data) if ua.Action == Action_RoundEnd { data = getTypeDesc(ua.Data) } else if ua.Action == Action_OutCard { data = getCardHex(ua.Data) if len(ua.ExtData) > 0 && !isAllZero(ua.ExtData) { data = fmt.Sprintf("%s%v", data, ua.ExtData) } } else if ua.Action == Action_Buy { data = getBuyActionDesc(ua.Data) } else if ua.Action == Action_ChooseTrump { data = getChooseTrumpDesc(ua.Data) } else if ua.Action == Action_CloseOrOpen { data = getCloseOrOpenDesc(ua.Data) } else if ua.Action == Action_Gawah { data = fmt.Sprintf("Gawah Chair[%d]", ua.Data) } else if ua.Action == Action_Double { data = getBuyActionDesc(ua.Data) } else if ua.Action == Action_Reshuffle { data = getReshuffleDesc(ua.Data) } else if ua.Action == Action_CorrectionStart { data = fmt.Sprintf("CorrectMode:%s", getCorrectModeDesc(ua.Data)) } else if ua.Action == Action_Sawa { data = "CallSawa" } else if ua.Action == Action_CorrectionFinish { data = "[" + getCorrectTypeDesc(ua.Data) + "]" + getCardsHex(ua.ExtData) } if ua.Action == Action_GameEnd { log.Debug(" ------ game end %s", fmt.Sprintf("[%d,%d,%d,%d]", ua.Data/1000000000, (ua.Data/1000000)%1000, (ua.Data/1000)%1000, ua.Data%1000)) return } if ua.Action == Action_RoundEnd { log.Debug(" ------- %s", data) return } if ua.Action == Action_RoundStart { log.Debug(" Chair[%d] Cards:%s", ua.ChairId, getCardsHex(ua.ExtData)) return } log.Debug(" Chair[%d],Action[%s],Data[%s]", ua.ChairId, getActionDesc(ua.Action), data) } func isValidChair(chairId int) bool { return chairId >= 0 && chairId < CHAIR_COUNT } func isPreviousChair(preChair, curChair int) bool { return (preChair+CHAIR_COUNT-1)%CHAIR_COUNT == curChair } func isFriendChair(chair1, chair2 int) bool { return (chair1+2)%CHAIR_COUNT == chair2 } func getFriendChair(chair int) int { return (chair + 2) % CHAIR_COUNT } func getPreviousChair(chair int) int { return (chair + CHAIR_COUNT - 1) % CHAIR_COUNT } func getNextChair(curChair int) int { return (curChair + 1) % CHAIR_COUNT } func isSameTeam(chair1, chair2 int) bool { return chair1%2 == chair2%2 } type perRoundScores struct { positionScore0 int positionScore1 int } func getScoreHistoryDesc(scoreHistory []perRoundScores) string { ret := "" for i := 0; i < len(scoreHistory); i++ { ret += "[" ret += fmt.Sprintf("%d", scoreHistory[i].positionScore0) ret += ": " ret += fmt.Sprintf("%d", scoreHistory[i].positionScore1) ret += "]" if i != (len(scoreHistory) - 1) { ret += ", " } } return ret } const ( Surrender_None = 0 // 等待队友确认 Surrender_Success = 1 // 成功 Surrender_Failed = 2 // 失败 ) // 投降信息 type SurrenderInfo struct { WhoseSurrender int // 谁发起的投降 SurrenderResult int // 投降结果 isSurrendering bool // 是否正在投降 } func newSurrenderInfo() *SurrenderInfo { di := new(SurrenderInfo) di.initData() return di } func (di *SurrenderInfo) initData() { di.WhoseSurrender = -1 di.SurrenderResult = Surrender_None di.isSurrendering = false } func (di *SurrenderInfo) getSurrenderInfo() string { d, _ := json.Marshal(di) return string(d) } func (di *SurrenderInfo) canDoSurrender(chairId int) bool { if !di.isSurrendering || !isValidChair(di.WhoseSurrender) { return false } return isSameTeam(chairId, di.WhoseSurrender) && di.SurrenderResult == Surrender_None } func (di *SurrenderInfo) isSurrenderEnd() bool { if !di.isSurrendering || !isValidChair(di.WhoseSurrender) { return false } return di.SurrenderResult == Surrender_Success } const ( RobotType_Cautious = iota RobotType_LAGs RobotType_Playfulness ) type weightInfo struct { chair int weight int } type robotChatActionList struct { round int action []robotChatAction } type robotChatAction struct { count int chatType int } func (r *robotChatAction) getDesc() string { switch r.chatType { case RobotChatAction_MuiltyTimeOut: return "有两次超过5秒没操作" case RobotChatAction_LoseOverFive: return "连续5回合没得分" case RobotChatAction_LoseOverEighty: return "回合结束相差超过80分" case RobotChatAction_FriendGetBigProject: return "队友获得50项目或以上时" case RobotChatAction_SelfWinOverFive: return "玩家连续五回合得分" case RobotChatAction_GoodHandCard: return "拿到牌之后,我赢的分数高的概率很高" case RobotChatAction_BadHandCard: return "手里的牌大部分都是10一下" case RobotChatAction_EmenyGetBigProject: return "对手拿到了100-400项目" case RobotChatAction_EnterRoom: return "发你好" case RobotChatAction_Reply: return "回你好" case RobotChatAction_FriendWinOverFive: return "机器人队友连续5回合赢" } return "" } func getRobotChatDesc(chatList []robotChatAction) string { ret := "[" for _, v := range chatList { ret += fmt.Sprintf("%s: %d", v.getDesc(), v.count) ret += " " } ret += "]" return ret }