| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673 |
- package gamelogic
- import (
- "encoding/json"
- "math/rand"
- "bet24.com/log"
- )
- const (
- GAMEID = 82
- GAME_NAME = "ludo"
- 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
- )
- const SEC_READY = 15 * 1000 //准备计时
- const SEC_MATCH = 4000 //匹配计时 4000 匹配动画时间
- // const SEC_DICE = 4000 //投掷骰子超时3s 后端多设置1秒延迟
- // const SEC_MOVE = 10000 //移动飞机超时10s
- 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 = 57 // 终点
- const (
- Action_Dice = iota // 投掷骰子
- Action_Move // 移动飞机
- Action_Drop // 2 弃牌
- Action_Invalid
- )
- // 飞机是否安全
- func isSafe(pos int) bool {
- //还未出发
- if pos == 0 {
- return true
- }
- if pos >= 52 {
- return true
- }
- //第九格和第14格为安全区
- return (pos-9)%13 == 0 || (pos-14)%13 == 0
- }
- // 查找位置存在危险的棋子
- func (gs *GameScene) getNnsafePlane(chairId int) int {
- unsafePlane := make(map[int]int)
- myPlanes := gs.Players[chairId].Planes
- for k, v := range myPlanes {
- if isSafe(v.Position) || !v.CanMove {
- continue
- }
- for k1, v1 := range myPlanes {
- if k == k1 {
- continue
- }
- //叠棋
- if v.Position == v1.Position && v.Id != v1.Id {
- continue
- }
- unsafePlane[v.Id] = v.Position
- }
- }
- planeId := -1
- if len(unsafePlane) > 0 {
- farthest := -1 //最远的
- for unsafeId, unsafePosition := range unsafePlane {
- for i := 0; i < CHAIR_COUNT; i++ {
- //不跟自己比
- if i == chairId {
- continue
- }
- //无效的玩家过滤
- if !gs.Players[i].IsValid {
- continue
- }
- if gs.Players[i].Dropped {
- continue
- }
- if gs.Players[i].checkChairPosition(chairId, unsafePosition) == 2 {
- //有危险
- if unsafePosition > farthest {
- farthest = unsafePosition
- planeId = unsafeId
- }
- }
- }
- }
- }
- return planeId
- }
- // 是否相撞
- func isCrash(chair1, pos1, chair2, pos2 int) bool {
- if isSafe(pos1) || isSafe(pos2) {
- return false
- }
- return (chair1*13+pos1)%52 == (chair2*13+pos2)%52
- }
- // 是否有效座位
- 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
- }
- // 操作结果
- type ActionResult struct {
- Action int //动作信息
- Number int //骰子数值
- PlaneId int //操作棋子ID
- Position int //棋子移动位置
- CrashedChairId int //被撞击座位
- CrashedPlaneId int //被撞击棋子ID
- }
- func (ua *userAction) dump() {
- log.Debug(" Chair[%d],Action[%d],PlaneId[%d],Number[%d]", ua.ChairId, ua.Action, ua.PlaneId, ua.Number)
- }
- type GameScene struct {
- Index int
- Phase int
- WhoseTurn int
- LastTurn int
- ActionResult ActionResult // 操作信息
- Players []PlayerInfo
- LeftSec int //本阶段剩余时间
- userActions []userAction
- pool int
- base int
- }
- func newGameScene() *GameScene {
- gs := new(GameScene)
- gs.initData()
- return gs
- }
- // 弃权
- 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 {
- //log.Debug("checkPositionIsCrashed myPlaneCount[%d] ", myPlaneCount)
- 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) calculateWeight(pos, number int) int {
- weight := -1
- relativePos := (pos + number) % 13 //移动后的相对坐标
- //计算距离
- safeDistance := (relativePos - 9) % 13
- if safeDistance <= (relativePos-14)%13 {
- safeDistance = (relativePos - 14) % 13
- }
- if pos < 52 && pos+number >= 52 {
- //点数可以进入52以后的 5
- weight = 5
- } else if safeDistance == 0 {
- //可以进入到安全区 4
- weight = 4
- } else if (pos-9)%13 == 0 || (pos-14)%13 == 0 {
- //已经在安全区优先动 1
- weight = 1
- } else {
- //其他情况 2 包括已经进入最后阶段 pos >= 52
- weight = 2
- }
- return weight
- }
- // 检查棋子移动后结果
- 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()
- //如果自己的步数还未大于20 则不触发
- if myPositionCount < 20 {
- 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()
- //检查自己是否落后对手超过10步以上了
- if ret >= myPositionCount+10 {
- return true
- }
- }
- return false
- }
- 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 gs.Players[chairId].AutoOut && !isValidPlane(planeId) {
- farthest := -1 //最远的
- for _, v := range gs.Players[chairId].Planes {
- if !v.CanMove {
- continue
- }
- //先出机场中棋子
- if v.Position == 0 {
- planeId = v.Id
- break
- }
- //其次走最后是距离
- if v.Position > farthest {
- farthest = v.Position
- planeId = v.Id
- }
- }
- } else if isRobot && !isValidPlane(planeId) {
- //机器人
- tempWeight := -1 //权重
- weightGroup := []int{}
- unsafePlaneId := gs.getNnsafePlane(chairId) //危险的棋子
- number := gs.Players[chairId].Number
- for _, v := range gs.Players[chairId].Planes {
- //排除不能移动的棋子
- if !v.CanMove {
- continue
- }
- weight := -1
- //可以攻击优先
- if v.Position != 0 && v.Position < 52 && v.Position+number < 52 {
- total, _, _ := gs.checkPositionIsCrashed(chairId, v.Position+number, v.Id)
- if total == 1 {
- planeId = v.Id
- break
- }
- } else if v.Position == 0 {
- //其次出机场中棋子
- weight = 7
- }
- if weight == -1 && v.Id == unsafePlaneId {
- //再考虑走危险的棋子
- weight = 6
- }
- if weight == -1 {
- weight = gs.calculateWeight(v.Position, number) //机器人计算权重
- if weight <= 2 {
- //2以下存在变数
- moveResult := gs.checkPlaneMoveResult(chairId, v.Position+number) //移动后的结果
- if moveResult == 1 {
- //如果是可以追击则权重上升
- weight++
- } else if moveResult == 2 {
- if number != 6 {
- //超过去有危险
- weight = 0
- } else if weight == 2 && number == 6 && gs.Players[chairId].ContinueSixPoint >= 1 {
- //摆脱机会不大 哪怕本次摇出6 接下来再次出6无法移动
- weight = 1
- }
- }
- }
- }
- if weight > tempWeight {
- weightGroup = []int{}
- weightGroup = append(weightGroup, v.Id)
- tempWeight = weight
- } else if weight == tempWeight {
- weightGroup = append(weightGroup, v.Id)
- }
- }
- //找到优先级更高的棋子时 根据权重选择
- if !isValidPlane(planeId) && len(weightGroup) > 0 {
- count := len(weightGroup)
- if count > 1 {
- //随机选一个
- planeId = weightGroup[rand.Intn(count)]
- } else {
- planeId = weightGroup[0]
- }
- }
- }
- ok, movePlaneId, isReach, moveStepCount := gs.Players[chairId].canMovePlane(planeId)
- if !ok {
- return false, "wrong movement", isNextChair, stepCount
- }
- 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++
- }
- //没有攻击玩家 并且 点数不是6 或者 6的次数没有超过限制 则到下一个玩家操作
- if total != 1 && (gs.Players[chairId].Number != 6 || gs.Players[chairId].ContinueSixPoint > 2) {
- isNextChair = true
- }
- }
- gs.ActionResult.PlaneId = planeId
- 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() {
- //如果都不移动则判断是否快到终点
- gs.Players[chairId].isWillWin()
- isNextChair = true
- } else {
- gs.LastTurn = gs.WhoseTurn
- }
- } 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) initActionResult() {
- gs.ActionResult = ActionResult{Action: -1, Number: -1, PlaneId: -1, Position: -1, CrashedChairId: -1, CrashedPlaneId: -1}
- return
- }
- func (gs *GameScene) initData() {
- gs.Index = 0
- gs.pool = 0
- gs.WhoseTurn = -1
- gs.Phase = Phase_Free
- gs.WhoseTurn = 0
- gs.LastTurn = 0
- gs.initActionResult()
- gs.Players = make([]PlayerInfo, CHAIR_COUNT)
- gs.userActions = []userAction{}
- }
- 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) dump(tableId int) {
- log.Debug("====GameScene Table[%d]====", tableId)
- log.Debug(" Phase[%d],WhoseTurn[%d],LastTurn[%d]",
- gs.Phase, gs.WhoseTurn, gs.LastTurn)
- log.Debug(" Users:%d", gs.getValidUserCount())
- for i := 0; i < CHAIR_COUNT; i++ {
- if !gs.Players[i].IsValid {
- continue
- }
- gs.Players[i].dump(i)
- }
- if len(gs.userActions) > 0 {
- log.Debug(" Actions:%d", len(gs.userActions))
- for _, v := range gs.userActions {
- v.dump()
- }
- }
- }
- func (gs *GameScene) getPlayerCount() int {
- ret := 0
- for i := 0; i < CHAIR_COUNT; i++ {
- if !gs.Players[i].IsValid {
- continue
- }
- 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人游戏第一名拿3份
- if totalPlayerCount == 4 {
- multiple = 3
- }
- //大家都弃权了 第一名拿全部池子内金币
- 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
- }
|