| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751 |
- package gamelogic
- import (
- "encoding/json"
- "bet24.com/log"
- badge "bet24.com/servers/micros/badge/proto"
- )
- const (
- GAMEID = 38
- GAME_NAME = "quickludo"
- CHAIR_COUNT = 4
- PLANE_COUNT = 4
- )
- const (
- Phase_Free = iota
- Phase_Match //匹配结束
- Phase_Dice //掷骰子中
- Phase_Move //移动飞机中
- Phase_End //结束
- )
- const (
- ScoreType_Bet = iota
- ScoreType_End
- ScoreType_Return
- )
- const (
- TIMER_READY_0 = iota
- TIMER_READY_1
- TIMER_READY_2
- TIMER_READY_3
- TIMER_MATCH
- TIMER_GAME
- TIMER_ROBOT
- TIMER_ADD_ROBOT
- TIMER_REMOVE_ROBOT
- TIMER_QUIT_0
- TIMER_QUIT_1
- TIMER_QUIT_2
- TIMER_QUIT_3
- )
- const SEC_READY = 30 * 1000 //准备计时
- const SEC_MATCH = 4000 //匹配计时 4000 匹配动画时间
- const SEC_DELAY = 2000 //动画等待时间2s
- const SEC_MOVE_TIME = 300 //移动一格时间
- const SEC_NEXT_CHAIR = 500 //换人间隔
- const SEC_AUTO = 2000 //托管超时
- const SEC_ROBOT_CHAT = 5000
- const MAX_STEP = 58 // 终点
- const SAFE_START = 53 //安全通道起始坐标
- const LOOP_START = 52 //转圈起始位
- const LOOP_END = 51 //转圈截止位
- const (
- Action_Dice = iota // 投掷骰子
- Action_Move // 移动飞机
- Action_Drop // 2 弃牌
- Action_Invalid
- )
- // 飞机是否安全
- func isSafe(pos int) bool {
- //还未出发
- if pos == 0 {
- return true
- }
- if pos >= SAFE_START {
- return true
- }
- //第九格和第14格为安全区
- return (pos-9)%13 == 0 || (pos-14)%13 == 0
- }
- // 查找优先级高的棋子
- func (gs *GameScene) getPriorityPlane(chairId int) (int, int) {
- unsafePlane := make(map[int]int)
- farthestPlane := -1
- farthestPosition := -1
- myPlanes := gs.Players[chairId].Planes
- for k, v := range myPlanes {
- if !v.CanMove {
- continue
- }
- if v.Position > farthestPosition {
- farthestPlane = v.Id
- farthestPosition = v.Position
- }
- if isSafe(v.Position) {
- continue
- }
- for k1, v1 := range myPlanes {
- if k == k1 || !v1.CanMove {
- continue
- }
- //叠棋
- if v.Position == v1.Position && v.Id != v1.Id {
- continue
- }
- unsafePlane[v.Id] = v.Position
- }
- }
- unsafePlaneId := -1
- if len(unsafePlane) > 0 {
- farthest := -1 //最远的
- for unsafeId, unsafePosition := range unsafePlane {
- for i := 0; i < CHAIR_COUNT; i++ {
- //不跟自己比
- if i == chairId || !gs.Players[i].IsValid || gs.Players[i].Dropped {
- continue
- }
- if gs.Players[i].checkChairPosition(chairId, unsafePosition) == 2 {
- //有危险
- if unsafePosition > farthest {
- farthest = unsafePosition
- unsafePlaneId = unsafeId
- }
- }
- }
- }
- }
- //存在风险的棋子和最远端的棋子
- return unsafePlaneId, farthestPlane
- }
- // 检查起飞区附近是否有敌人
- func (gs *GameScene) checkTookOffArea(chairId int) bool {
- //检查全部玩家
- for i := 0; i < CHAIR_COUNT; i++ {
- if i == chairId || !gs.Players[i].IsValid || gs.Players[i].Dropped {
- continue
- }
- //坐标转换后在1-10之间的都算有棋子
- for _, v := range gs.Players[i].Planes {
- if v.Position == 0 {
- continue
- }
- if gs.Players[i].convertPlanePosition(v.Position) <= 10 {
- //有棋子
- return true
- }
- }
- }
- return false
- }
- // 是否相撞
- func isCrash(chair1, pos1, chair2, pos2 int) bool {
- if pos1 == 0 || pos2 == 0 || isSafe(pos1) || isSafe(pos2) {
- return false
- }
- return (chair1*13+pos1)%LOOP_START == (chair2*13+pos2)%LOOP_START
- }
- // 是否有效座位
- func isValidChair(chairId int) bool {
- return chairId >= 0 && chairId < CHAIR_COUNT
- }
- // 是否有效棋子
- func isValidPlane(planeId int) bool {
- return planeId >= 0 && planeId < PLANE_COUNT
- }
- // 是否有效点数
- func isValidPoint(point int) bool {
- return point >= 1 && point <= 6
- }
- type userAction struct {
- ChairId int
- Action int
- Number int
- PlaneId int
- }
- func (ua *userAction) dump() {
- log.Debug(" Chair[%d],Action[%d],PlaneId[%d],Number[%d]", ua.ChairId, ua.Action, ua.PlaneId, ua.Number)
- }
- // 弃权
- func (p *PlayerInfo) drop() {
- p.Dropped = true
- }
- // 检测位置是否撞机
- func (gs *GameScene) checkPositionIsCrashed(chairId, position, planeId int) (total int, tempCrashed *Plane, crashedPlayer *PlayerInfo) {
- total = 0
- //自己的棋子数量
- myPlaneCount := 0
- for i := 0; i < CHAIR_COUNT; i++ {
- //不能忽略自己的棋子 有可能自己的棋子和对方的棋子形成了安全区
- if i == chairId {
- planeCount := gs.Players[i].checkSamePositionCount(position, planeId)
- myPlaneCount += planeCount
- if planeCount != 0 {
- break
- }
- continue
- }
- //无效的玩家过滤
- if !gs.Players[i].IsValid {
- continue
- }
- if gs.Players[i].Dropped {
- continue
- }
- //检查加上步数后是否和该玩家的棋子是否相撞
- crashed := gs.Players[i].checkCrashed(chairId, position)
- crashedCount := len(crashed)
- if crashedCount < 1 {
- continue
- }
- total += crashedCount
- //如果当前用户有一个被踩则暂存
- if crashedCount == 1 {
- tempCrashed = crashed[0]
- crashedPlayer = &gs.Players[i]
- }
- }
- if total != 0 && myPlaneCount >= 1 {
- total += myPlaneCount
- }
- return total, tempCrashed, crashedPlayer
- }
- // 检测前方是否有敌人的棋子 (两人场判断)
- func (gs *GameScene) checkFrontHasEnemy(chairId, position, planeId int) bool {
- //敌人的座位号
- enemyChairId := (chairId + 2) % CHAIR_COUNT
- planeCount := gs.Players[chairId].checkSamePositionCount(position, planeId)
- if planeCount != 0 {
- return false
- }
- crashed := gs.Players[enemyChairId].checkCrashed(chairId, position)
- crashedCount := len(crashed)
- return crashedCount == 1
- }
- // 得到不能攻击的点数
- func (gs *GameScene) getUnAttackPoint(chairId int) []int {
- //临时标记
- points := []int{1, 2, 3, 4, 5, 6}
- unAttackPoint := make([]int, 0)
- attackPoint := make([]int, 0)
- for _, v := range gs.Players[chairId].Planes {
- //没有出机场或者已经到终点和快到终点的过滤
- if v.Position == 0 || v.Position == MAX_STEP || v.Position >= SAFE_START {
- continue
- }
- for _, p := range points {
- //可以攻击 则替换点数
- isHasEnemy := gs.checkFrontHasEnemy(chairId, v.Position+p, v.Id)
- if isHasEnemy {
- attackPoint = append(attackPoint, p)
- }
- }
- }
- if len(attackPoint) == 0 {
- return unAttackPoint
- }
- //剔除可以攻击的点数
- for _, v := range points {
- isHas := false
- for _, p := range attackPoint {
- if v == p {
- isHas = true
- break
- }
- }
- if !isHas {
- unAttackPoint = append(unAttackPoint, v)
- }
- }
- return unAttackPoint
- }
- // 检查棋子移动后结果
- func (gs *GameScene) checkPlaneMoveResult(chairId, position int) int {
- //临时标记
- temp := -1
- for i := 0; i < CHAIR_COUNT; i++ {
- //不跟自己比
- if i == chairId {
- continue
- }
- //无效的玩家过滤
- if !gs.Players[i].IsValid {
- continue
- }
- if gs.Players[i].Dropped {
- continue
- }
- //检查自己的棋子加上步数后 是否超过或者接近对手的棋子
- ret := gs.Players[i].checkChairPosition(chairId, position)
- if ret == 2 {
- //有危险则不在判断
- temp = ret
- break
- }
- if ret > temp {
- temp = ret
- }
- }
- return temp
- }
- // 检查自己是否落后对手超过10步以上了(只考虑最远端的)
- func (gs *GameScene) checkIsBehind(chairId int) bool {
- myPositionCount := gs.Players[chairId].calculatePosition()
- if myPositionCount < 10 {
- return false
- }
- for i := 0; i < CHAIR_COUNT; i++ {
- if i == chairId {
- continue
- }
- if !gs.Players[i].IsValid {
- continue
- }
- if gs.Players[i].Dropped {
- continue
- }
- //检查自己的棋子加上步数后 是否超过或者接近对手的棋子
- ret := gs.Players[i].calculatePosition()
- if ret >= myPositionCount+10 {
- return true
- }
- }
- return false
- }
- // 计算机器人飞机移动权重
- func (gs *GameScene) calcRobotPlaneWeight(chairId, number int) int {
- // 初始化变量
- tempWeight := -1 // 权重
- weightPlaneId := -1 // 权重最高的棋子
- planeId := -1
- farthest := -1 // 最远的
- tookOffCount, leaveCount := gs.Players[chairId].checkTookOffCount()
- checkTookOffArea := false
- if number == 6 && tookOffCount < 4 && leaveCount == tookOffCount {
- //摇到6 并且还有没有起飞的 已经起飞的都离开起飞区域了
- checkTookOffArea = gs.checkTookOffArea(chairId)
- }
- // 获取通道是否打开的信息
- openChannel := gs.Players[chairId].OpenChannel
- // 获取危险的棋子的编号
- unsafePlaneId, farthestPlane := gs.getPriorityPlane(chairId)
- for _, v := range gs.Players[chairId].Planes {
- if !v.CanMove {
- continue
- }
- id := v.Id
- position := v.Position
- movePosition := position + number
- if openChannel && movePosition == MAX_STEP {
- planeId = id
- break
- }
- weight := -1 // 权重
- relativePos := (movePosition) % 13 //移动后的相对坐标
- //计算距离
- safeDistance := (relativePos - 9) % 13
- if safeDistance <= (relativePos-14)%13 {
- safeDistance = (relativePos - 14) % 13
- }
- if movePosition == 48 {
- //最后一个安全区
- weight = 7
- } else if id == unsafePlaneId || safeDistance == 0 {
- weight = 6
- }
- if position == 0 && (checkTookOffArea || (tookOffCount == 1 && !openChannel)) {
- weight = 5
- }
- if openChannel {
- if movePosition >= LOOP_START {
- weight = 9
- }
- } else {
- if position == LOOP_END || movePosition > LOOP_END {
- weight = 0
- }
- }
- //判断是否可以攻击
- if position != 0 && (weight == -1 || weight == 6) {
- total, _, _ := gs.checkPositionIsCrashed(chairId, movePosition, id)
- if total == 1 {
- weight = 8
- }
- }
- if weight == -1 {
- weight = 3
- if position == 0 {
- weight = 2
- }
- if weight != 2 {
- moveResult := gs.checkPlaneMoveResult(chairId, movePosition) //移动后的结果
- if moveResult == 1 {
- //如果是可以追击则权重上升
- weight++
- } else if moveResult == 2 {
- if number != 6 {
- //超过去有危险
- weight = 1
- } else if gs.Players[chairId].ContinueSixPoint >= 1 {
- //摆脱机会不大 哪怕本次摇出6 接下来再次出6无法移动
- weight = 2
- if (position-9)%13 == 0 || (position-14)%13 == 0 {
- //已经在安全区
- weight = 1
- }
- }
- } else {
- //最远端的
- if id == farthestPlane {
- weight = 6
- }
- }
- }
- }
- if weight > tempWeight || (weight == tempWeight && position > farthest) {
- farthest = position
- weightPlaneId = id
- if weight > tempWeight {
- tempWeight = weight
- }
- }
- }
- //找到优先级更高的棋子时 根据权重选择
- if !isValidPlane(planeId) && isValidPlane(weightPlaneId) {
- planeId = weightPlaneId
- }
- return planeId
- }
- func (gs *GameScene) addAction(chairId, action, number, planeId int, isRobot bool) (bool, string, bool, int) {
- gs.initActionResult()
- //是否下一个玩家
- var isNextChair = false
- var stepCount = 0
- //判断数据有效性
- if chairId != gs.WhoseTurn {
- return false, "wrong turn", isNextChair, stepCount
- }
- if action == gs.Players[chairId].LastAction {
- return false, "wrong action", isNextChair, stepCount
- }
- //移动飞机
- if action == Action_Move {
- var isReach = false
- if !isValidPlane(planeId) {
- planeId = gs.tryMovePlane(chairId, isRobot)
- }
- ok, movePlaneId, isReach, moveStepCount, oldPosition := gs.Players[chairId].canMovePlane(planeId)
- if !ok {
- return false, "wrong movement", isNextChair, moveStepCount
- }
- stepCount = moveStepCount
- planeId = movePlaneId
- //获胜额外获得一次机会
- if !isReach {
- // 检测撞机
- total, tempCrashed, crashedPlayer := gs.checkPositionIsCrashed(chairId, gs.Players[chairId].Planes[planeId].Position, planeId)
- //一个区域如果出现2个棋子,无论是否同玩家 则为安全区 否则则攻击
- if total == 1 {
- gs.ActionResult.CrashedChairId = crashedPlayer.chairId //被撞击座位
- gs.ActionResult.CrashedPlaneId = tempCrashed.Id //被撞击棋子ID
- tempCrashed.Position = 0
- tempCrashed.CanMove = false
- //玩家被踩加一
- crashedPlayer.Death++
- //踩别人的棋子额外获得一次机会
- gs.Players[chairId].Kills++
- //打开通道
- gs.Players[chairId].OpenChannel = true
- //获得徽章
- badgeParam := badge.Scope{GameName: GAME_NAME}
- go badge.DoAction(gs.Players[chairId].userId, badge.Action_Game_Kill, 1, badgeParam)
- }
- //没有攻击玩家 并且 点数不是6 或者 6的次数没有超过限制 则到下一个玩家操作
- if total != 1 && (gs.Players[chairId].Number != 6 || gs.Players[chairId].ContinueSixPoint > 2) {
- isNextChair = true
- }
- }
- gs.ActionResult.PlaneId = planeId
- gs.ActionResult.OldPosition = oldPosition
- gs.ActionResult.Position = gs.Players[chairId].Planes[planeId].Position
- } else {
- if !isValidPoint(number) {
- return false, "wrong point", isNextChair, stepCount
- }
- //掷骰子
- ok := gs.Players[chairId].setRollNumber(number)
- gs.ActionResult.Number = number
- if ok {
- if !gs.Players[chairId].isCanMove() {
- isNextChair = true
- } else {
- gs.LastTurn = gs.WhoseTurn
- }
- //如果通道打开了则判断
- if gs.Players[chairId].OpenChannel {
- //如果都不移动则判断是否快到终点
- gs.Players[chairId].isWillWin()
- }
- } else {
- //如果点数设置失败(6的次数超过限制) 则直接跳过
- isNextChair = true
- }
- }
- gs.Players[chairId].LastAction = action
- gs.ActionResult.Action = action
- if isNextChair {
- //重置6次数 要在此处重置 否则会导致到终点后重新计算
- gs.Players[chairId].ContinueSixPoint = 0
- }
- gs.userActions = append(gs.userActions, userAction{ChairId: chairId, Action: action, Number: number, PlaneId: planeId})
- return true, "", isNextChair, stepCount
- }
- // 尝试移动飞机
- func (gs *GameScene) tryMovePlane(chairId int, isRobot bool) int {
- var planeId int = -1
- //判断是否被封禁
- openChannel := gs.Players[chairId].OpenChannel
- number := gs.Players[chairId].Number
- if isRobot {
- //机器人计算权重
- planeId = gs.calcRobotPlaneWeight(chairId, number)
- return planeId
- }
- //判断前端操作指令是否合理 托管
- if gs.Players[chairId].AutoOut {
- farthest := -1 //最远的
- tempPlaneId := -1 //临时标记
- for _, v := range gs.Players[chairId].Planes {
- if !v.CanMove {
- continue
- }
- //先出机场中棋子
- if v.Position == 0 {
- planeId = v.Id
- break
- }
- //其次走最后是距离
- if v.Position > farthest {
- if !openChannel && (v.Position == LOOP_END || v.Position+number > LOOP_END) {
- tempPlaneId = v.Id
- } else {
- farthest = v.Position
- planeId = v.Id
- }
- }
- }
- if !isValidPlane(planeId) {
- planeId = tempPlaneId
- }
- }
- return planeId
- }
- func (gs *GameScene) initActionResult() {
- gs.ActionResult = ActionResult{Action: -1, Number: -1, PlaneId: -1, OldPosition: -1, Position: -1, CrashedChairId: -1, CrashedPlaneId: -1}
- }
- func (gs *GameScene) getValidUserCount() int {
- ret := 0
- for i := 0; i < CHAIR_COUNT; i++ {
- if gs.Players[i].IsValid {
- ret++
- }
- }
- return ret
- }
- func (gs *GameScene) getScore(userId int) int {
- for i := 0; i < CHAIR_COUNT; i++ {
- if gs.Players[i].userId == userId {
- return gs.Players[i].Score
- }
- }
- return 0
- }
- func (gs *GameScene) getScene(chairId int, player bool) string {
- d, _ := json.Marshal(gs)
- return string(d)
- }
- // 找到下一个操作者 顺时针操作
- func (gs *GameScene) nextChair() {
- gs.LastTurn = gs.WhoseTurn
- for i := 1; i < CHAIR_COUNT; i++ {
- next := (gs.LastTurn + i) % CHAIR_COUNT
- if !gs.Players[next].IsValid {
- continue
- }
- if gs.Players[next].Dropped {
- continue
- }
- gs.Players[next].LastAction = Action_Move
- gs.WhoseTurn = next
- break
- }
- }
- func (gs *GameScene) addWinner(chairId int) (int, bool) {
- leftPlayerCount := 0
- totalPlayerCount := 0
- leftChair := CHAIR_COUNT
- for i := 0; i < CHAIR_COUNT; i++ {
- if !gs.Players[i].IsValid {
- continue
- }
- totalPlayerCount++
- if chairId == i {
- continue
- }
- if gs.Players[i].Dropped {
- gs.Players[i].Place = -1
- continue
- }
- leftPlayerCount++
- leftChair = i
- }
- multiple := totalPlayerCount
- //4人游戏第一名拿4份 第二名没有奖励
- if totalPlayerCount == 4 {
- multiple = 4
- }
- //大家都弃权了 第一名拿全部池子内金币
- if leftPlayerCount == 0 {
- gs.Players[chairId].Score = gs.pool
- gs.Players[chairId].Place = 1
- return gs.Players[chairId].Score, true
- }
- if gs.pool >= gs.base*multiple {
- gs.Players[chairId].Score = gs.base * multiple
- gs.Players[chairId].Place = 1
- } else if gs.pool <= gs.base && totalPlayerCount > 2 {
- //池子还有钱并且是4人游戏 则第二名拿剩下的一份
- gs.Players[chairId].Score = gs.pool
- gs.Players[chairId].Place = 2
- }
- gs.pool -= gs.Players[chairId].Score
- if gs.pool > 0 && leftPlayerCount == 1 {
- gs.Players[leftChair].Score = gs.pool
- gs.Players[leftChair].Place = 2
- gs.pool = 0
- }
- return gs.Players[chairId].Score, gs.pool == 0
- }
- // commands
- const (
- CMD_ROOMINFO = "CMD_ROOMINFO"
- CMD_ACTION = "CMD_ACTION"
- CMD_TABLECHAT = "CMD_TABLECHAT"
- CMD_CANCLE_AUTO = "CMD_CANCLE_AUTO"
- CMD_DROP = "CMD_DROP"
- )
- type CmdAction struct {
- Action int
- PlaneId int
- }
- // 广播Drop
- type CmdDrop struct {
- ChairId int
- }
|