GameLogic.go 11 KB

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