GameLogic.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402
  1. package slotpanda
  2. import (
  3. "encoding/json"
  4. "math/rand"
  5. "os"
  6. "strconv"
  7. "sync"
  8. "time"
  9. "bet24.com/log"
  10. "bet24.com/servers/micros/slotsservice/handler/slotcommon"
  11. "bet24.com/servers/micros/slotsservice/handler/slotcommon/betlevel"
  12. "bet24.com/servers/micros/slotsservice/handler/slotcommon/slotcount"
  13. )
  14. var gameLogic *GameLogic
  15. func getGameLogic() *GameLogic {
  16. if gameLogic == nil {
  17. gameLogic = new(GameLogic)
  18. gameLogic.ctor()
  19. }
  20. return gameLogic
  21. }
  22. type GameLogic_config struct {
  23. Slots []Slot
  24. WinShapes []WinShape
  25. BetLevels []betlevel.BetLevel
  26. JackpotAmount int
  27. }
  28. type GameLogic struct {
  29. GameLogic_config
  30. MinBet int
  31. MaxBet int
  32. Wins []Win
  33. TaxRate int
  34. lock *sync.RWMutex
  35. slotmgr *slotmanager
  36. Test []TestSlots
  37. FreeSlotChanged []FreeSlotChange
  38. userFreeSpins map[int]*FreeSpinInfo
  39. slotCounts *slotcount.MultipleSlotCountManager
  40. bonusSlotCount *slotcount.MultipleSlotCountManager
  41. freeSlotCount *slotcount.MultipleSlotCountManager
  42. jackpotManager *JackpotManager
  43. betLevelManager *betlevel.BetLevelManager
  44. MinJackpotGameCount int
  45. lockRemove *sync.RWMutex
  46. removingFreespins map[int]int64
  47. slotCommon *slotcommon.Slotcommon
  48. }
  49. func (this *GameLogic) ctor() {
  50. log.Color(LogColor, "slotpanda GameLogic.run")
  51. this.lock = &sync.RWMutex{}
  52. this.userFreeSpins = make(map[int]*FreeSpinInfo)
  53. this.removingFreespins = make(map[int]int64)
  54. this.lockRemove = &sync.RWMutex{}
  55. rand.Seed(time.Now().UnixNano())
  56. this.slotCommon = slotcommon.NewSlotCommon(GAMEID, GAME_NAME, this.TaxRate)
  57. this.initData()
  58. time.AfterFunc(5*time.Minute, this.refreshData)
  59. time.AfterFunc(time.Second, this.test)
  60. go this.checkRemoveUser()
  61. }
  62. func (this *GameLogic) initUserFreeSpin(userId int) {
  63. this.lock.Lock()
  64. _, ok := this.userFreeSpins[userId]
  65. if !ok {
  66. this.userFreeSpins[userId] = newFreeSpinInfo(userId, this.freeSlotCount, this.WinShapes, this.Wins, this.slotmgr, this.FreeSlotChanged)
  67. }
  68. this.lock.Unlock()
  69. }
  70. func (this *GameLogic) getSlotConfig(userId int) string {
  71. // update user's ping
  72. // ...
  73. this.initUserFreeSpin(userId)
  74. d, _ := json.Marshal(this.GameLogic_config)
  75. return string(d)
  76. }
  77. func (this *GameLogic) dump(param1, param2 string) {
  78. }
  79. func (sm *GameLogic) checkRemoveUser() {
  80. time.AfterFunc(slotcommon.DELAY_CHECK*time.Second, sm.checkRemoveUser)
  81. var toRemove []int
  82. latestRemoveTime := time.Now().Unix() - slotcommon.DELAY_REMOVE
  83. sm.lockRemove.RLock()
  84. for k, v := range sm.removingFreespins {
  85. if v < latestRemoveTime {
  86. toRemove = append(toRemove, k)
  87. }
  88. }
  89. sm.lockRemove.RUnlock()
  90. if len(toRemove) == 0 {
  91. return
  92. }
  93. sm.lockRemove.Lock()
  94. for _, v := range toRemove {
  95. delete(sm.removingFreespins, v)
  96. }
  97. sm.lockRemove.Unlock()
  98. sm.lock.Lock()
  99. for _, v := range toRemove {
  100. delete(sm.userFreeSpins, v)
  101. }
  102. sm.lock.Unlock()
  103. }
  104. func (this *GameLogic) test() {
  105. if len(os.Args) < 2 {
  106. return
  107. }
  108. if os.Args[1] != "panda" {
  109. return
  110. }
  111. init := 1000000000
  112. if len(os.Args) >= 3 {
  113. init, _ = strconv.Atoi(os.Args[2])
  114. }
  115. initGold := init
  116. betAmount := 250000
  117. if len(os.Args) >= 4 {
  118. betAmount, _ = strconv.Atoi(os.Args[3])
  119. }
  120. maxCount := 1000
  121. if len(os.Args) >= 5 {
  122. maxCount, _ = strconv.Atoi(os.Args[4])
  123. }
  124. log.Debug("testing panda slot initGold[%d] betAmount[%d] maxCount[%d]", initGold, betAmount, maxCount)
  125. testId := 0
  126. freeWin := 0
  127. bonusWin := 0
  128. jackpotWin := 0
  129. bonusCount := 0
  130. this.userFreeSpins[testId] = newFreeSpinInfo(testId, this.freeSlotCount, this.WinShapes, this.Wins, this.slotmgr, this.FreeSlotChanged)
  131. resultSet := make([]SlotPanda_Result, maxCount)
  132. var wg sync.WaitGroup
  133. for i := 0; i < maxCount; i++ {
  134. wg.Add(1)
  135. go func(idx int) {
  136. defer wg.Done()
  137. r, _ := this.getResult(testId, betAmount, 0, false)
  138. resultSet[idx] = r
  139. }(i)
  140. }
  141. wg.Wait()
  142. log.Color(LogColor, "测试结果已准备")
  143. for i := 0; i < maxCount; i++ {
  144. free, freeResult, _ := this.useFreeSpin(testId)
  145. if free {
  146. i--
  147. freeWin += freeResult.getWinAmount()
  148. initGold += freeResult.getWinAmount()
  149. continue
  150. }
  151. if initGold < betAmount {
  152. break
  153. }
  154. initGold -= betAmount
  155. result := resultSet[i]
  156. bonusAmount := 0
  157. if result.Bonus != nil {
  158. bonusCount++
  159. bonusAmount = result.Bonus.BonusResult + result.Bonus.JackpotResult
  160. bonusWin += result.Bonus.BonusResult
  161. jackpotWin += result.Bonus.JackpotResult
  162. }
  163. initGold += result.WinAmount + bonusAmount
  164. }
  165. log.Debug("pandaslot left[%d],win[%d],BonusCount[%d],Bonus[%d],Jackpot[%d],Free[%d]", initGold, initGold-init, bonusCount, bonusWin, jackpotWin, freeWin)
  166. }
  167. func (this *GameLogic) refreshData() {
  168. go this.initData()
  169. time.AfterFunc(5*time.Minute, this.refreshData)
  170. }
  171. func (this *GameLogic) initData() {
  172. data, err := os.ReadFile("slotconf/slotpanda.json")
  173. if err != nil {
  174. log.Error("read slotpanda.json failed")
  175. }
  176. this.lock.Lock()
  177. err = json.Unmarshal(data, &this)
  178. if err != nil {
  179. log.Error("Unmarshal slotpanda.json failed err:%v", err)
  180. this.lock.Unlock()
  181. return
  182. }
  183. this.slotmgr = newSlotManager(this.Slots)
  184. this.slotCounts = slotcount.NewMultipleSlotCountManager("slotpanda_normal_mul") // newSlotCounts(this.SlotCounts)
  185. this.bonusSlotCount = slotcount.NewMultipleSlotCountManager("slotpanda_bonus_mul") //newSlotCounts(this.BonusSlotCounts)
  186. this.freeSlotCount = slotcount.NewMultipleSlotCountManager("slotpanda_free_mul") //newSlotCounts(this.FreeSlotCounts)
  187. this.betLevelManager = betlevel.NewBetLevelManagerByData(this.BetLevels)
  188. this.jackpotManager = newJackpotManager(this.slotmgr.getBonusSlotId(), this.MinJackpotGameCount,
  189. this.bonusSlotCount, this.betLevelManager)
  190. this.lock.Unlock()
  191. if len(this.Test) > 0 {
  192. log.Debug("slotpanda testing")
  193. result, _ := this.getResult(0, 100, 0, false)
  194. d, _ := json.Marshal(result)
  195. log.Debug("%s", string(d))
  196. }
  197. }
  198. func (this *GameLogic) getUserSlotCountNormal(userId int, betAmount int) *slotcount.SlotCountManager {
  199. level := 0
  200. return this.slotCounts.GetMgr(level)
  201. }
  202. func (this *GameLogic) get15Slots(betAmount int, userId int) []Slot {
  203. // 如果有测试
  204. if len(this.Test) > 0 {
  205. count := len(this.Test)
  206. log.Debug("test count = %d", count)
  207. ret := make([]Slot, 15)
  208. slotIDs := this.Test[rand.Intn(len(this.Test))].Slots
  209. if len(slotIDs) < 15 {
  210. for i := 0; i < 15-len(slotIDs); i++ {
  211. slotIDs = append(slotIDs, 1+rand.Intn(10))
  212. }
  213. }
  214. log.Release("slotpanda get15Slots 测试中 %v", slotIDs)
  215. for i := 0; i < 15; i++ {
  216. ret[i] = this.slotmgr.getSlot(slotIDs[i])
  217. }
  218. return ret
  219. }
  220. betLevel := this.betLevelManager.GetLevel(betAmount)
  221. sc := this.getUserSlotCountNormal(userId, betAmount)
  222. ret := make([]Slot, RESULT_COUNT)
  223. slotIDs := sc.Get15Slots(betLevel) //this.slotCounts.get15Slots(isFree, level)
  224. for i := 0; i < 15; i++ {
  225. ret[i] = this.slotmgr.getSlot(slotIDs[i])
  226. }
  227. return ret
  228. }
  229. func (this *GameLogic) getOneResult(slotID, slotCount, shapeID int) *Result {
  230. for _, v := range this.Wins {
  231. if v.SlotID != slotID {
  232. continue
  233. }
  234. for _, rate := range v.Rates {
  235. if rate.Count == slotCount {
  236. return &Result{SlotID: slotID, SlotCount: slotCount, WinShapeID: shapeID, WinRate: rate.Win}
  237. }
  238. }
  239. }
  240. return nil
  241. }
  242. func (this *GameLogic) getResult(userId int, betAmount int, controlType int, isChipRoom bool) (SlotPanda_Result, bool) {
  243. var ret SlotPanda_Result
  244. if betAmount <= 0 {
  245. log.Release("slotpanda.GameLogic.GetResult betAmount = %d", betAmount)
  246. return ret, true
  247. }
  248. ret.Slots = make([]int, RESULT_COUNT)
  249. ret.BetAmount = betAmount
  250. // 取15个result
  251. slots := this.get15Slots(betAmount, userId)
  252. for k, v := range slots {
  253. ret.Slots[k] = v.SlotID
  254. if v.SlotID == this.slotmgr.getBonusSlotId() {
  255. ret.Slots[k] = this.jackpotManager.getRandomMultiple()*100 + v.SlotID
  256. }
  257. }
  258. // 计算结果
  259. shapeCount := len(this.WinShapes)
  260. for k, v := range this.WinShapes {
  261. // 查看每条连线的数量
  262. slotID, slotCount, _ := v.getCount(slots)
  263. // 查看结果
  264. result := this.getOneResult(slotID, slotCount, k)
  265. if result != nil {
  266. // 中奖了
  267. ret.WinAmount += betAmount * result.WinRate / shapeCount
  268. ret.Lines = append(ret.Lines, *result)
  269. }
  270. }
  271. if controlType == 1 && (ret.WinAmount > betAmount || this.jackpotManager.isJackpot(ret.Slots)) {
  272. return this.getResult(userId, betAmount, controlType, isChipRoom)
  273. }
  274. if controlType == 2 && ret.WinAmount < betAmount && !this.jackpotManager.isJackpot(ret.Slots) {
  275. return this.getResult(userId, betAmount, controlType, isChipRoom)
  276. }
  277. // Bonus
  278. _, bonus := this.jackpotManager.addJackpot(ret.Slots, betAmount, userId, isChipRoom)
  279. ret.Bonus = bonus
  280. // 已经产生bonus,不产生freespin
  281. if bonus != nil {
  282. return ret, true
  283. }
  284. // 检查是否产生freespin
  285. this.lock.RLock()
  286. _, ok := this.userFreeSpins[userId]
  287. if !ok {
  288. log.Debug("Not Exist User:%d", userId)
  289. this.lock.RUnlock()
  290. return ret, false
  291. }
  292. ret.FreeSpinInner, ret.FreeSpinOuter = this.userFreeSpins[userId].initFree(betAmount, ret.Slots, isChipRoom)
  293. this.lock.RUnlock()
  294. if controlType == 1 && ret.FreeSpinInner > 0 {
  295. // 控制的时候,需要把已产生的免费次数去掉
  296. log.Release("control and reset free")
  297. this.userFreeSpins[userId].removeFreeTimes(ret.FreeSpinInner * ret.FreeSpinOuter)
  298. return this.getResult(userId, betAmount, controlType, isChipRoom)
  299. }
  300. return ret, true
  301. }
  302. func (this *GameLogic) getSlots() []Slot {
  303. this.lock.RLock()
  304. defer this.lock.RUnlock()
  305. return this.Slots
  306. }
  307. func (this *GameLogic) getWinShapes() []WinShape {
  308. this.lock.RLock()
  309. defer this.lock.RUnlock()
  310. return this.WinShapes
  311. }
  312. func (this *GameLogic) useFreeSpin(userId int) (bool, FreeSpinResult, bool) {
  313. this.lock.Lock()
  314. defer this.lock.Unlock()
  315. t, ok := this.userFreeSpins[userId]
  316. if !ok {
  317. return false, FreeSpinResult{}, false
  318. }
  319. return t.useFreeSpin()
  320. }
  321. func (this *GameLogic) getFreeSpinTime(userId int) int {
  322. this.lock.RLock()
  323. defer this.lock.RUnlock()
  324. t, ok := this.userFreeSpins[userId]
  325. if !ok {
  326. return 0
  327. }
  328. return t.getFreeCount()
  329. }
  330. func (this *GameLogic) getResultDesc(result SlotPanda_Result) string {
  331. var total struct {
  332. Slots []int
  333. Lines []int
  334. }
  335. total.Slots = result.Slots
  336. for _, v := range result.Lines {
  337. total.Lines = append(total.Lines, v.WinShapeID)
  338. }
  339. //total.Special = result.BetAmount * (result.Special.WinRate1 + result.Special.WinRate2)
  340. data, _ := json.Marshal(total)
  341. return string(data)
  342. }