gamedefs.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673
  1. package gamelogic
  2. import (
  3. "encoding/json"
  4. "math/rand"
  5. "bet24.com/log"
  6. )
  7. const (
  8. GAMEID = 82
  9. GAME_NAME = "ludo"
  10. CHAIR_COUNT = 4
  11. PLANE_COUNT = 4
  12. )
  13. const (
  14. Phase_Free = iota
  15. Phase_Match //匹配结束
  16. Phase_Dice //掷骰子中
  17. Phase_Move //移动飞机中
  18. Phase_End //结束
  19. )
  20. const (
  21. ScoreType_Bet = iota
  22. ScoreType_End
  23. ScoreType_Return
  24. )
  25. const (
  26. TIMER_READY_0 = iota
  27. TIMER_READY_1
  28. TIMER_READY_2
  29. TIMER_READY_3
  30. TIMER_MATCH
  31. TIMER_GAME
  32. TIMER_ROBOT
  33. TIMER_ADD_ROBOT
  34. TIMER_REMOVE_ROBOT
  35. )
  36. const SEC_READY = 15 * 1000 //准备计时
  37. const SEC_MATCH = 4000 //匹配计时 4000 匹配动画时间
  38. // const SEC_DICE = 4000 //投掷骰子超时3s 后端多设置1秒延迟
  39. // const SEC_MOVE = 10000 //移动飞机超时10s
  40. const SEC_DELAY = 2000 //动画等待时间2s
  41. const SEC_MOVE_TIME = 300 //移动一格时间
  42. const SEC_NEXT_CHAIR = 500 //换人间隔
  43. const SEC_AUTO = 2000 //托管超时
  44. const SEC_ROBOT_CHAT = 5000
  45. const MAX_STEP = 57 // 终点
  46. const (
  47. Action_Dice = iota // 投掷骰子
  48. Action_Move // 移动飞机
  49. Action_Drop // 2 弃牌
  50. Action_Invalid
  51. )
  52. // 飞机是否安全
  53. func isSafe(pos int) bool {
  54. //还未出发
  55. if pos == 0 {
  56. return true
  57. }
  58. if pos >= 52 {
  59. return true
  60. }
  61. //第九格和第14格为安全区
  62. return (pos-9)%13 == 0 || (pos-14)%13 == 0
  63. }
  64. // 查找位置存在危险的棋子
  65. func (gs *GameScene) getNnsafePlane(chairId int) int {
  66. unsafePlane := make(map[int]int)
  67. myPlanes := gs.Players[chairId].Planes
  68. for k, v := range myPlanes {
  69. if isSafe(v.Position) || !v.CanMove {
  70. continue
  71. }
  72. for k1, v1 := range myPlanes {
  73. if k == k1 {
  74. continue
  75. }
  76. //叠棋
  77. if v.Position == v1.Position && v.Id != v1.Id {
  78. continue
  79. }
  80. unsafePlane[v.Id] = v.Position
  81. }
  82. }
  83. planeId := -1
  84. if len(unsafePlane) > 0 {
  85. farthest := -1 //最远的
  86. for unsafeId, unsafePosition := range unsafePlane {
  87. for i := 0; i < CHAIR_COUNT; i++ {
  88. //不跟自己比
  89. if i == chairId {
  90. continue
  91. }
  92. //无效的玩家过滤
  93. if !gs.Players[i].IsValid {
  94. continue
  95. }
  96. if gs.Players[i].Dropped {
  97. continue
  98. }
  99. if gs.Players[i].checkChairPosition(chairId, unsafePosition) == 2 {
  100. //有危险
  101. if unsafePosition > farthest {
  102. farthest = unsafePosition
  103. planeId = unsafeId
  104. }
  105. }
  106. }
  107. }
  108. }
  109. return planeId
  110. }
  111. // 是否相撞
  112. func isCrash(chair1, pos1, chair2, pos2 int) bool {
  113. if isSafe(pos1) || isSafe(pos2) {
  114. return false
  115. }
  116. return (chair1*13+pos1)%52 == (chair2*13+pos2)%52
  117. }
  118. // 是否有效座位
  119. func isValidChair(chairId int) bool {
  120. return chairId >= 0 && chairId < CHAIR_COUNT
  121. }
  122. // 是否有效棋子
  123. func isValidPlane(planeId int) bool {
  124. return planeId >= 0 && planeId < PLANE_COUNT
  125. }
  126. // 是否有效点数
  127. func isValidPoint(point int) bool {
  128. return point >= 1 && point <= 6
  129. }
  130. type userAction struct {
  131. ChairId int
  132. Action int
  133. Number int
  134. PlaneId int
  135. }
  136. // 操作结果
  137. type ActionResult struct {
  138. Action int //动作信息
  139. Number int //骰子数值
  140. PlaneId int //操作棋子ID
  141. Position int //棋子移动位置
  142. CrashedChairId int //被撞击座位
  143. CrashedPlaneId int //被撞击棋子ID
  144. }
  145. func (ua *userAction) dump() {
  146. log.Debug(" Chair[%d],Action[%d],PlaneId[%d],Number[%d]", ua.ChairId, ua.Action, ua.PlaneId, ua.Number)
  147. }
  148. type GameScene struct {
  149. Index int
  150. Phase int
  151. WhoseTurn int
  152. LastTurn int
  153. ActionResult ActionResult // 操作信息
  154. Players []PlayerInfo
  155. LeftSec int //本阶段剩余时间
  156. userActions []userAction
  157. pool int
  158. base int
  159. }
  160. func newGameScene() *GameScene {
  161. gs := new(GameScene)
  162. gs.initData()
  163. return gs
  164. }
  165. // 弃权
  166. func (p *PlayerInfo) drop() {
  167. p.Dropped = true
  168. }
  169. // 检测位置是否撞机
  170. func (gs *GameScene) checkPositionIsCrashed(chairId, position, planeId int) (total int, tempCrashed *Plane, crashedPlayer *PlayerInfo) {
  171. total = 0
  172. //自己的棋子数量
  173. myPlaneCount := 0
  174. for i := 0; i < CHAIR_COUNT; i++ {
  175. //不能忽略自己的棋子 有可能自己的棋子和对方的棋子形成了安全区
  176. if i == chairId {
  177. planeCount := gs.Players[i].checkSamePositionCount(position, planeId)
  178. myPlaneCount += planeCount
  179. if planeCount != 0 {
  180. //log.Debug("checkPositionIsCrashed myPlaneCount[%d] ", myPlaneCount)
  181. break
  182. }
  183. continue
  184. }
  185. //无效的玩家过滤
  186. if !gs.Players[i].IsValid {
  187. continue
  188. }
  189. if gs.Players[i].Dropped {
  190. continue
  191. }
  192. //检查加上步数后是否和该玩家的棋子是否相撞
  193. crashed := gs.Players[i].checkCrashed(chairId, position)
  194. crashedCount := len(crashed)
  195. if crashedCount < 1 {
  196. continue
  197. }
  198. total += crashedCount
  199. //如果当前用户有一个被踩则暂存
  200. if crashedCount == 1 {
  201. tempCrashed = crashed[0]
  202. crashedPlayer = &gs.Players[i]
  203. }
  204. }
  205. if total != 0 && myPlaneCount >= 1 {
  206. total += myPlaneCount
  207. }
  208. return total, tempCrashed, crashedPlayer
  209. }
  210. // 计算飞机移动权重
  211. func (gs *GameScene) calculateWeight(pos, number int) int {
  212. weight := -1
  213. relativePos := (pos + number) % 13 //移动后的相对坐标
  214. //计算距离
  215. safeDistance := (relativePos - 9) % 13
  216. if safeDistance <= (relativePos-14)%13 {
  217. safeDistance = (relativePos - 14) % 13
  218. }
  219. if pos < 52 && pos+number >= 52 {
  220. //点数可以进入52以后的 5
  221. weight = 5
  222. } else if safeDistance == 0 {
  223. //可以进入到安全区 4
  224. weight = 4
  225. } else if (pos-9)%13 == 0 || (pos-14)%13 == 0 {
  226. //已经在安全区优先动 1
  227. weight = 1
  228. } else {
  229. //其他情况 2 包括已经进入最后阶段 pos >= 52
  230. weight = 2
  231. }
  232. return weight
  233. }
  234. // 检查棋子移动后结果
  235. func (gs *GameScene) checkPlaneMoveResult(chairId, position int) int {
  236. //临时标记
  237. temp := -1
  238. for i := 0; i < CHAIR_COUNT; i++ {
  239. //不跟自己比
  240. if i == chairId {
  241. continue
  242. }
  243. //无效的玩家过滤
  244. if !gs.Players[i].IsValid {
  245. continue
  246. }
  247. if gs.Players[i].Dropped {
  248. continue
  249. }
  250. //检查自己的棋子加上步数后 是否超过或者接近对手的棋子
  251. ret := gs.Players[i].checkChairPosition(chairId, position)
  252. if ret == 2 {
  253. //有危险则不在判断
  254. temp = ret
  255. break
  256. }
  257. if ret > temp {
  258. temp = ret
  259. }
  260. }
  261. return temp
  262. }
  263. // 检查自己是否落后对手超过10步以上了
  264. func (gs *GameScene) checkIsBehind(chairId int) bool {
  265. //自己的棋子总和
  266. myPositionCount := gs.Players[chairId].calculatePosition()
  267. //如果自己的步数还未大于20 则不触发
  268. if myPositionCount < 20 {
  269. return false
  270. }
  271. for i := 0; i < CHAIR_COUNT; i++ {
  272. //不跟自己比
  273. if i == chairId {
  274. continue
  275. }
  276. //无效的玩家过滤
  277. if !gs.Players[i].IsValid {
  278. continue
  279. }
  280. if gs.Players[i].Dropped {
  281. continue
  282. }
  283. //检查自己的棋子加上步数后 是否超过或者接近对手的棋子
  284. ret := gs.Players[i].calculatePosition()
  285. //检查自己是否落后对手超过10步以上了
  286. if ret >= myPositionCount+10 {
  287. return true
  288. }
  289. }
  290. return false
  291. }
  292. func (gs *GameScene) addAction(chairId, action, number, planeId int, isRobot bool) (bool, string, bool, int) {
  293. gs.initActionResult()
  294. //是否下一个玩家
  295. var isNextChair = false
  296. var stepCount = 0
  297. //判断数据有效性
  298. if chairId != gs.WhoseTurn {
  299. return false, "wrong turn", isNextChair, stepCount
  300. }
  301. if action == gs.Players[chairId].LastAction {
  302. return false, "wrong action", isNextChair, stepCount
  303. }
  304. //移动飞机
  305. if action == Action_Move {
  306. var isReach = false
  307. //判断前端操作指令是否合理 托管
  308. if gs.Players[chairId].AutoOut && !isValidPlane(planeId) {
  309. farthest := -1 //最远的
  310. for _, v := range gs.Players[chairId].Planes {
  311. if !v.CanMove {
  312. continue
  313. }
  314. //先出机场中棋子
  315. if v.Position == 0 {
  316. planeId = v.Id
  317. break
  318. }
  319. //其次走最后是距离
  320. if v.Position > farthest {
  321. farthest = v.Position
  322. planeId = v.Id
  323. }
  324. }
  325. } else if isRobot && !isValidPlane(planeId) {
  326. //机器人
  327. tempWeight := -1 //权重
  328. weightGroup := []int{}
  329. unsafePlaneId := gs.getNnsafePlane(chairId) //危险的棋子
  330. number := gs.Players[chairId].Number
  331. for _, v := range gs.Players[chairId].Planes {
  332. //排除不能移动的棋子
  333. if !v.CanMove {
  334. continue
  335. }
  336. weight := -1
  337. //可以攻击优先
  338. if v.Position != 0 && v.Position < 52 && v.Position+number < 52 {
  339. total, _, _ := gs.checkPositionIsCrashed(chairId, v.Position+number, v.Id)
  340. if total == 1 {
  341. planeId = v.Id
  342. break
  343. }
  344. } else if v.Position == 0 {
  345. //其次出机场中棋子
  346. weight = 7
  347. }
  348. if weight == -1 && v.Id == unsafePlaneId {
  349. //再考虑走危险的棋子
  350. weight = 6
  351. }
  352. if weight == -1 {
  353. weight = gs.calculateWeight(v.Position, number) //机器人计算权重
  354. if weight <= 2 {
  355. //2以下存在变数
  356. moveResult := gs.checkPlaneMoveResult(chairId, v.Position+number) //移动后的结果
  357. if moveResult == 1 {
  358. //如果是可以追击则权重上升
  359. weight++
  360. } else if moveResult == 2 {
  361. if number != 6 {
  362. //超过去有危险
  363. weight = 0
  364. } else if weight == 2 && number == 6 && gs.Players[chairId].ContinueSixPoint >= 1 {
  365. //摆脱机会不大 哪怕本次摇出6 接下来再次出6无法移动
  366. weight = 1
  367. }
  368. }
  369. }
  370. }
  371. if weight > tempWeight {
  372. weightGroup = []int{}
  373. weightGroup = append(weightGroup, v.Id)
  374. tempWeight = weight
  375. } else if weight == tempWeight {
  376. weightGroup = append(weightGroup, v.Id)
  377. }
  378. }
  379. //找到优先级更高的棋子时 根据权重选择
  380. if !isValidPlane(planeId) && len(weightGroup) > 0 {
  381. count := len(weightGroup)
  382. if count > 1 {
  383. //随机选一个
  384. planeId = weightGroup[rand.Intn(count)]
  385. } else {
  386. planeId = weightGroup[0]
  387. }
  388. }
  389. }
  390. ok, movePlaneId, isReach, moveStepCount := gs.Players[chairId].canMovePlane(planeId)
  391. if !ok {
  392. return false, "wrong movement", isNextChair, stepCount
  393. }
  394. stepCount = moveStepCount
  395. planeId = movePlaneId
  396. //获胜额外获得一次机会
  397. if !isReach {
  398. // 检测撞机
  399. total, tempCrashed, crashedPlayer := gs.checkPositionIsCrashed(chairId, gs.Players[chairId].Planes[planeId].Position, planeId)
  400. //一个区域如果出现2个棋子,无论是否同玩家 则为安全区 否则则攻击
  401. if total == 1 {
  402. gs.ActionResult.CrashedChairId = crashedPlayer.chairId //被撞击座位
  403. gs.ActionResult.CrashedPlaneId = tempCrashed.Id //被撞击棋子ID
  404. tempCrashed.Position = 0
  405. tempCrashed.CanMove = false
  406. //玩家被踩加一
  407. crashedPlayer.Death++
  408. //踩别人的棋子额外获得一次机会
  409. gs.Players[chairId].Kills++
  410. }
  411. //没有攻击玩家 并且 点数不是6 或者 6的次数没有超过限制 则到下一个玩家操作
  412. if total != 1 && (gs.Players[chairId].Number != 6 || gs.Players[chairId].ContinueSixPoint > 2) {
  413. isNextChair = true
  414. }
  415. }
  416. gs.ActionResult.PlaneId = planeId
  417. gs.ActionResult.Position = gs.Players[chairId].Planes[planeId].Position
  418. } else {
  419. if !isValidPoint(number) {
  420. return false, "wrong point", isNextChair, stepCount
  421. }
  422. //掷骰子
  423. ok := gs.Players[chairId].setRollNumber(number)
  424. gs.ActionResult.Number = number
  425. if ok {
  426. if !gs.Players[chairId].isCanMove() {
  427. //如果都不移动则判断是否快到终点
  428. gs.Players[chairId].isWillWin()
  429. isNextChair = true
  430. } else {
  431. gs.LastTurn = gs.WhoseTurn
  432. }
  433. } else {
  434. //如果点数设置失败(6的次数超过限制) 则直接跳过
  435. isNextChair = true
  436. }
  437. }
  438. gs.Players[chairId].LastAction = action
  439. gs.ActionResult.Action = action
  440. if isNextChair {
  441. //重置6次数 要在此处重置 否则会导致到终点后重新计算
  442. gs.Players[chairId].ContinueSixPoint = 0
  443. }
  444. gs.userActions = append(gs.userActions, userAction{ChairId: chairId, Action: action, Number: number, PlaneId: planeId})
  445. return true, "", isNextChair, stepCount
  446. }
  447. func (gs *GameScene) initActionResult() {
  448. gs.ActionResult = ActionResult{Action: -1, Number: -1, PlaneId: -1, Position: -1, CrashedChairId: -1, CrashedPlaneId: -1}
  449. return
  450. }
  451. func (gs *GameScene) initData() {
  452. gs.Index = 0
  453. gs.pool = 0
  454. gs.WhoseTurn = -1
  455. gs.Phase = Phase_Free
  456. gs.WhoseTurn = 0
  457. gs.LastTurn = 0
  458. gs.initActionResult()
  459. gs.Players = make([]PlayerInfo, CHAIR_COUNT)
  460. gs.userActions = []userAction{}
  461. }
  462. func (gs *GameScene) getValidUserCount() int {
  463. ret := 0
  464. for i := 0; i < CHAIR_COUNT; i++ {
  465. if gs.Players[i].IsValid {
  466. ret++
  467. }
  468. }
  469. return ret
  470. }
  471. func (gs *GameScene) dump(tableId int) {
  472. log.Debug("====GameScene Table[%d]====", tableId)
  473. log.Debug(" Phase[%d],WhoseTurn[%d],LastTurn[%d]",
  474. gs.Phase, gs.WhoseTurn, gs.LastTurn)
  475. log.Debug(" Users:%d", gs.getValidUserCount())
  476. for i := 0; i < CHAIR_COUNT; i++ {
  477. if !gs.Players[i].IsValid {
  478. continue
  479. }
  480. gs.Players[i].dump(i)
  481. }
  482. if len(gs.userActions) > 0 {
  483. log.Debug(" Actions:%d", len(gs.userActions))
  484. for _, v := range gs.userActions {
  485. v.dump()
  486. }
  487. }
  488. }
  489. func (gs *GameScene) getPlayerCount() int {
  490. ret := 0
  491. for i := 0; i < CHAIR_COUNT; i++ {
  492. if !gs.Players[i].IsValid {
  493. continue
  494. }
  495. ret++
  496. }
  497. return ret
  498. }
  499. func (gs *GameScene) getScore(userId int) int {
  500. for i := 0; i < CHAIR_COUNT; i++ {
  501. if gs.Players[i].userId == userId {
  502. return gs.Players[i].Score
  503. }
  504. }
  505. return 0
  506. }
  507. func (gs *GameScene) getScene(chairId int, player bool) string {
  508. d, _ := json.Marshal(gs)
  509. return string(d)
  510. }
  511. // 找到下一个操作者 顺时针操作
  512. func (gs *GameScene) nextChair() {
  513. gs.LastTurn = gs.WhoseTurn
  514. for i := 1; i < CHAIR_COUNT; i++ {
  515. next := (gs.LastTurn + i) % CHAIR_COUNT
  516. if !gs.Players[next].IsValid {
  517. continue
  518. }
  519. if gs.Players[next].Dropped {
  520. continue
  521. }
  522. gs.Players[next].LastAction = Action_Move
  523. gs.WhoseTurn = next
  524. break
  525. }
  526. }
  527. func (gs *GameScene) addWinner(chairId int) (int, bool) {
  528. leftPlayerCount := 0
  529. totalPlayerCount := 0
  530. leftChair := CHAIR_COUNT
  531. for i := 0; i < CHAIR_COUNT; i++ {
  532. if !gs.Players[i].IsValid {
  533. continue
  534. }
  535. totalPlayerCount++
  536. if chairId == i {
  537. continue
  538. }
  539. if gs.Players[i].Dropped {
  540. gs.Players[i].Place = -1
  541. continue
  542. }
  543. leftPlayerCount++
  544. leftChair = i
  545. }
  546. multiple := totalPlayerCount
  547. //4人游戏第一名拿3份
  548. if totalPlayerCount == 4 {
  549. multiple = 3
  550. }
  551. //大家都弃权了 第一名拿全部池子内金币
  552. if leftPlayerCount == 0 {
  553. gs.Players[chairId].Score = gs.pool
  554. gs.Players[chairId].Place = 1
  555. return gs.Players[chairId].Score, true
  556. }
  557. if gs.pool >= gs.base*multiple {
  558. gs.Players[chairId].Score = gs.base * multiple
  559. gs.Players[chairId].Place = 1
  560. } else if gs.pool <= gs.base && totalPlayerCount > 2 {
  561. //池子还有钱并且是4人游戏 则第二名拿剩下的一份
  562. gs.Players[chairId].Score = gs.pool
  563. gs.Players[chairId].Place = 2
  564. }
  565. gs.pool -= gs.Players[chairId].Score
  566. if gs.pool > 0 && leftPlayerCount == 1 {
  567. gs.Players[leftChair].Score = gs.pool
  568. gs.Players[leftChair].Place = 2
  569. gs.pool = 0
  570. }
  571. return gs.Players[chairId].Score, gs.pool == 0
  572. }
  573. // commands
  574. const (
  575. CMD_ROOMINFO = "CMD_ROOMINFO"
  576. CMD_ACTION = "CMD_ACTION"
  577. CMD_TABLECHAT = "CMD_TABLECHAT"
  578. CMD_CANCLE_AUTO = "CMD_CANCLE_AUTO"
  579. CMD_DROP = "CMD_DROP"
  580. )
  581. type CmdAction struct {
  582. Action int
  583. PlaneId int
  584. }
  585. // 广播Drop
  586. type CmdDrop struct {
  587. ChairId int
  588. }