GameLogic.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414
  1. package wildapeslot
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "math/rand"
  6. "os"
  7. "sort"
  8. "strconv"
  9. "sync"
  10. "time"
  11. "bet24.com/log"
  12. "bet24.com/servers/games/slotcommon"
  13. "bet24.com/servers/games/slotcommon/betlevel"
  14. "bet24.com/servers/games/slotcommon/usermanager"
  15. "bet24.com/utils"
  16. )
  17. const DELAY_REMOVE = 300 // 300秒后删除用户
  18. const DELAY_CHECK = 60 // 每分钟检查一次
  19. type userFreeSpin struct {
  20. freeSpinTime int // 免费次数
  21. lastBetAmount int
  22. fromAd bool
  23. isBonus bool
  24. }
  25. type GameLogic struct {
  26. AdBets []DragonSlot_FreeAdBet
  27. TaxRate int
  28. BonusFree []int
  29. BonusWild []int
  30. MinJackpot int
  31. MaxNoneFreeCount int
  32. ShapeConfig ShapeConfig
  33. lock *sync.RWMutex
  34. userFreeSpins map[int]*userFreeSpin
  35. freeSpin *FreeSpinInfo
  36. slotSink slotcommon.SlotSink
  37. multipleCountMgr *MultipleSlotCountManager
  38. //slotCountsAd *SlotCountManager
  39. betLevelManager *betlevel.BetLevelManager
  40. slotManager *SlotManager
  41. Test []TestSlots
  42. testWinAmount int
  43. slotCountEasy *SlotCountManager
  44. lockRemove *sync.RWMutex
  45. removingFreespins map[int]int64
  46. slotCommon *slotcommon.Slotcommon
  47. }
  48. func NewGameLogic(slotSink slotcommon.SlotSink) *GameLogic {
  49. obj := new(GameLogic)
  50. obj.lock = &sync.RWMutex{}
  51. obj.userFreeSpins = make(map[int]*userFreeSpin)
  52. obj.slotSink = slotSink
  53. obj.removingFreespins = make(map[int]int64)
  54. obj.lockRemove = &sync.RWMutex{}
  55. return obj
  56. }
  57. func (this *GameLogic) run() {
  58. log.Color(LogColor, "wildapeslot.GameLogic run")
  59. rand.Seed(time.Now().UnixNano())
  60. this.slotCommon = slotcommon.NewSlotCommon(this.slotSink, GAMEID, GAME_NAME, this.TaxRate, GAME_MESSAGE)
  61. this.initData()
  62. time.AfterFunc(5*time.Minute, this.refreshData)
  63. //this.test()
  64. go this.checkRemoveUser()
  65. }
  66. func (sm *GameLogic) checkRemoveUser() {
  67. time.AfterFunc(DELAY_CHECK*time.Second, sm.checkRemoveUser)
  68. var toRemove []int
  69. latestRemoveTime := time.Now().Unix() - DELAY_REMOVE
  70. sm.lockRemove.RLock()
  71. for k, v := range sm.removingFreespins {
  72. if v < latestRemoveTime {
  73. toRemove = append(toRemove, k)
  74. }
  75. }
  76. sm.lockRemove.RUnlock()
  77. if len(toRemove) == 0 {
  78. return
  79. }
  80. sm.lockRemove.Lock()
  81. for _, v := range toRemove {
  82. delete(sm.removingFreespins, v)
  83. }
  84. sm.lockRemove.Unlock()
  85. sm.lock.Lock()
  86. for _, v := range toRemove {
  87. delete(sm.userFreeSpins, v)
  88. }
  89. sm.lock.Unlock()
  90. }
  91. func (this *GameLogic) test() {
  92. defer utils.TimeCost(fmt.Sprintf("GameLogic.test"))()
  93. /*
  94. for _, v := range this.betLevelManager.levels {
  95. this.testLevel()
  96. }
  97. */
  98. testIndex, err := strconv.Atoi(os.Args[1])
  99. if err != nil {
  100. return
  101. }
  102. log.Color(LogColor, "wildapeslot.GameLogic test")
  103. levels := this.betLevelManager.Levels()
  104. v := levels[len(levels)-1-testIndex]
  105. this.testLevel(v.Bet)
  106. log.Color(LogColor, "wildapeslot.GameLogic test end")
  107. }
  108. func (this *GameLogic) testLevel(bet int) {
  109. m, _ := strconv.Atoi(os.Args[2])
  110. tc, _ := strconv.Atoi(os.Args[3])
  111. originAmount := bet * m
  112. log.Debug("开始测试 带入金额[%d] 最多测试次数[%d] 测试底注[%d]", originAmount, tc, bet)
  113. winAmount := 0
  114. testAmount := bet
  115. totalJackpot := 0
  116. testedCount := 0
  117. //betLevel, base := this.betLevelManager.getLevelAndBase(testAmount)
  118. betAmount := 0
  119. returnAmount := 0
  120. taxAmount := 0
  121. for i := 0; i < tc; i++ {
  122. if originAmount < bet {
  123. log.Debug("break %d < %d", originAmount, bet)
  124. break
  125. }
  126. betAmount += bet
  127. originAmount -= bet
  128. testedCount++
  129. //slots := this.slotCountManager.get15Slots(betLevel)
  130. //log.Color(LogColor, "test %d +++++++++++++++", i)
  131. //dumpSlots(slots)
  132. //results := this.slotManager.getResults(slots)
  133. this.testWinAmount = winAmount
  134. result := this.getResult(0, testAmount, false, false)
  135. winAmount += result.WinAmount
  136. winAmount -= bet
  137. if result.WinAmount-bet > 0 {
  138. taxAmount += int((result.WinAmount - bet) / 100 * this.TaxRate)
  139. }
  140. returnAmount = returnAmount + result.WinAmount
  141. //tc += result.FreeSpinCount
  142. //freeCount += result.FreeSpinCount
  143. //originAmount += result.FreeSpinCount * bet
  144. originAmount += result.WinAmount
  145. //log.Color(LogColor, "test %d end ------------------", i)
  146. }
  147. log.Color(LogColor, "转数[%d] 底注[%d] 奖池[%d],剩余[%d],输赢[%d] 税[%d] 返还率[%.4f] 概率分布%v",
  148. testedCount, bet, totalJackpot, originAmount, winAmount, taxAmount, float64(returnAmount)/float64(betAmount), this.multipleCountMgr.testCount)
  149. }
  150. func (this *GameLogic) refreshData() {
  151. go this.initData()
  152. time.AfterFunc(5*time.Minute, this.refreshData)
  153. }
  154. func (this *GameLogic) initData() {
  155. data, err := os.ReadFile("slotconf/wildapeslot_config.json")
  156. if err != nil {
  157. log.Error("read wildapeslot_config.json failed")
  158. }
  159. this.lock.Lock()
  160. err = json.Unmarshal(data, &this)
  161. if err != nil {
  162. log.Error("Unmarshal wildapeslot_config.json failed err:%v", err)
  163. this.lock.Unlock()
  164. return
  165. }
  166. this.ShapeConfig.loadConfig()
  167. this.freeSpin = newFreeSpinInfo()
  168. this.multipleCountMgr = newMultipleSlotCountManager()
  169. this.betLevelManager = betlevel.NewBetLevelManager("dfdcbetlevel", Msg_BetLevel)
  170. this.slotCommon.SetBetLevelManager(this.betLevelManager)
  171. this.slotManager = newSlotManager()
  172. sort.Slice(this.AdBets, func(i, j int) bool {
  173. return this.AdBets[i].AdCount > this.AdBets[j].AdCount
  174. })
  175. this.lock.Unlock()
  176. if this.MaxNoneFreeCount > 0 {
  177. usermanager.SetMaxNoneFreeCount(GAMEID, this.MaxNoneFreeCount)
  178. }
  179. if len(os.Args) >= 2 {
  180. time.AfterFunc(time.Second, this.test)
  181. }
  182. }
  183. func (this *GameLogic) getWinShapes() ShapeConfig {
  184. this.lock.RLock()
  185. defer this.lock.RUnlock()
  186. return this.ShapeConfig
  187. }
  188. func (this *GameLogic) getUserSlotCount(userId int) *SlotCountManager {
  189. return this.multipleCountMgr.getMgr(0)
  190. }
  191. func (this *GameLogic) getResult(userId int, betAmount int, isFree bool, isBonus bool) DragonSlot_Result {
  192. var ret DragonSlot_Result
  193. slotCountManager := this.getUserSlotCount(userId)
  194. if slotCountManager == nil {
  195. log.Release("GameLogic.getResult no slotcount manager")
  196. return ret
  197. }
  198. slotCount := RESULT_COUNT
  199. if isFree || isBonus {
  200. slotCount = FREE_RESULT_COUNT
  201. }
  202. ret.Slots = make([]int, slotCount)
  203. ret.BetAmount = betAmount
  204. level, base := this.betLevelManager.GetLevelAndBase(betAmount)
  205. // 取15个result
  206. if isFree || isBonus {
  207. ret.Slots = this.freeSpin.get15Slots(level, isFree, isBonus)
  208. } else {
  209. if len(this.Test) > 0 {
  210. ret.Slots = this.getTestSlots()
  211. } else {
  212. ret.Slots = slotCountManager.get15Slots(level, false, false)
  213. }
  214. }
  215. //ret.Lines = this.slotManager.getResults(ret.Slots)
  216. log.Debug("new Slots=%v", ret.Slots)
  217. this.fillWinLines(ret.Slots, isFree, isBonus, &ret)
  218. if len(ret.Lines) > 0 {
  219. for _, v := range ret.Lines {
  220. winAmount := int(float64(base) * v.WinRate)
  221. ret.WinAmount += winAmount
  222. }
  223. }
  224. ret.FreeApe = this.freeSpin.getFreeApe(ret.Slots)
  225. newBonus := this.fillBonusResult(ret.Slots, &ret)
  226. if ret.FreeApe {
  227. log.Debug("has FreeApe")
  228. this.addFreeSpin(userId, 3, betAmount, false, false)
  229. } else if newBonus {
  230. log.Debug("has newBonus")
  231. this.addFreeSpin(userId, ret.BonusFree, betAmount, false, true)
  232. }
  233. return ret
  234. }
  235. func (this *GameLogic) getTestSlots() []int {
  236. // 如果有测试
  237. count := len(this.Test)
  238. log.Debug("test count = %d", count)
  239. ret := make([]int, 15)
  240. slotIDs := this.Test[rand.Intn(len(this.Test))].Slots
  241. if len(slotIDs) < 15 {
  242. for i := 0; i < 15-len(slotIDs); i++ {
  243. slotIDs = append(slotIDs, 1+rand.Intn(10))
  244. }
  245. }
  246. log.Release("wildapeslot getTestSlots 测试中 %v", slotIDs)
  247. for i := 0; i < 15; i++ {
  248. ret[i] = slotIDs[i]
  249. }
  250. return ret
  251. }
  252. //判断修改bonus结果
  253. func (this *GameLogic) fillBonusResult(slots []int, ret *DragonSlot_Result) bool {
  254. count := this.slotManager.getBonusCount(slots)
  255. if count >= 3 {
  256. ret.BonusFree = this.BonusFree[rand.Intn(len(this.BonusFree))]
  257. ret.WildCount = this.BonusWild[rand.Intn(len(this.BonusWild))]
  258. return true
  259. }
  260. return false
  261. }
  262. //判断修改线路结算结果
  263. func (this *GameLogic) fillWinLines(slots []int, isFree bool, isBonus bool, ret *DragonSlot_Result) {
  264. var shapeCfgs []WinShape
  265. if isFree || isBonus {
  266. log.Debug("USE WinShape2 config")
  267. shapeCfgs = this.getWinShapes().WinShape2
  268. } else {
  269. shapeCfgs = this.getWinShapes().WinShape1
  270. }
  271. // 计算结果
  272. shapeCount := len(shapeCfgs)
  273. for k, v := range shapeCfgs {
  274. // 查看每条连线的数量
  275. slotID, slotCount, _ := v.getCount(slots)
  276. // 查看结果
  277. result := this.getOneResult(slotID, slotCount, k)
  278. if result != nil {
  279. // 中奖了
  280. ret.WinAmount += ret.BetAmount * int(result.WinRate) / shapeCount
  281. ret.Lines = append(ret.Lines, *result)
  282. }
  283. }
  284. }
  285. //返回结果结构体
  286. func (this *GameLogic) getOneResult(slotID, slotCount, shapeID int) *Result {
  287. if slotCount < 3 {
  288. return nil
  289. }
  290. for _, v := range this.slotManager.Slots {
  291. if v.SlotID != slotID {
  292. continue
  293. }
  294. var winFactor float64 = 1
  295. if slotCount == 3 {
  296. winFactor = v.Win3
  297. } else if slotCount == 4 {
  298. winFactor = v.Win4
  299. } else {
  300. winFactor = v.Win5
  301. }
  302. log.Debug("new Line Slotid=%d shape=%d", slotID, shapeID)
  303. return &Result{SlotID: slotID, SlotCount: slotCount, WinShape: shapeID, WinRate: winFactor}
  304. }
  305. return nil
  306. }
  307. func (this *GameLogic) useFreeSpin(userId int) (bool, int, bool, bool) {
  308. this.lock.Lock()
  309. defer this.lock.Unlock()
  310. t, ok := this.userFreeSpins[userId]
  311. if !ok || t.freeSpinTime <= 0 {
  312. return false, 0, false, false
  313. }
  314. t.freeSpinTime--
  315. log.Debug("免费次数剩余 %d isBonus=%t", t.freeSpinTime, t.isBonus)
  316. return t.isBonus, t.lastBetAmount, t.fromAd, t.freeSpinTime >= 0
  317. }
  318. func (this *GameLogic) getFreeSpinTime(userId int) int {
  319. this.lock.RLock()
  320. defer this.lock.RUnlock()
  321. t, ok := this.userFreeSpins[userId]
  322. if !ok {
  323. return 0
  324. }
  325. return t.freeSpinTime
  326. }
  327. func (this *GameLogic) userExit(userId int) {
  328. this.slotCommon.OnUserExit(userId)
  329. if this.getFreeSpinTime(userId) > 0 {
  330. this.lockRemove.Lock()
  331. this.removingFreespins[userId] = time.Now().Unix()
  332. this.lockRemove.Unlock()
  333. return
  334. }
  335. this.lock.Lock()
  336. defer this.lock.Unlock()
  337. delete(this.userFreeSpins, userId)
  338. }
  339. func (this *GameLogic) getBetDesc(betAmount int) string {
  340. return fmt.Sprintf("bet %d", betAmount)
  341. }
  342. func (this *GameLogic) getResultDesc(result DragonSlot_Result) string {
  343. var total struct {
  344. Slots []int
  345. Lines []int
  346. }
  347. total.Slots = result.Slots
  348. for _, v := range result.Lines {
  349. total.Lines = append(total.Lines, v.WinShape)
  350. }
  351. data, _ := json.Marshal(total)
  352. return string(data)
  353. }
  354. func (this *GameLogic) addFreeSpin(userId int, freeCount, bet int, fromAd bool, isBonus bool) {
  355. this.lock.Lock()
  356. this.userFreeSpins[userId] = &userFreeSpin{freeSpinTime: freeCount,
  357. lastBetAmount: bet, fromAd: fromAd, isBonus: isBonus}
  358. this.lock.Unlock()
  359. }