package slotpanda import ( "encoding/json" "math/rand" "os" "strconv" "sync" "time" "bet24.com/log" "bet24.com/servers/micros/slotsservice/handler/slotcommon" "bet24.com/servers/micros/slotsservice/handler/slotcommon/betlevel" "bet24.com/servers/micros/slotsservice/handler/slotcommon/slotcount" ) var gameLogic *GameLogic func getGameLogic() *GameLogic { if gameLogic == nil { gameLogic = new(GameLogic) gameLogic.ctor() } return gameLogic } type GameLogic_config struct { Slots []Slot WinShapes []WinShape BetLevels []betlevel.BetLevel JackpotAmount int } type GameLogic struct { GameLogic_config MinBet int MaxBet int Wins []Win TaxRate int lock *sync.RWMutex slotmgr *slotmanager Test []TestSlots FreeSlotChanged []FreeSlotChange userFreeSpins map[int]*FreeSpinInfo slotCounts *slotcount.MultipleSlotCountManager bonusSlotCount *slotcount.MultipleSlotCountManager freeSlotCount *slotcount.MultipleSlotCountManager jackpotManager *JackpotManager betLevelManager *betlevel.BetLevelManager MinJackpotGameCount int lockRemove *sync.RWMutex removingFreespins map[int]int64 slotCommon *slotcommon.Slotcommon } func (this *GameLogic) ctor() { log.Color(LogColor, "slotpanda GameLogic.run") this.lock = &sync.RWMutex{} this.userFreeSpins = make(map[int]*FreeSpinInfo) this.removingFreespins = make(map[int]int64) this.lockRemove = &sync.RWMutex{} rand.Seed(time.Now().UnixNano()) this.slotCommon = slotcommon.NewSlotCommon(GAMEID, GAME_NAME, this.TaxRate) this.initData() time.AfterFunc(5*time.Minute, this.refreshData) time.AfterFunc(time.Second, this.test) go this.checkRemoveUser() } func (this *GameLogic) initUserFreeSpin(userId int) { this.lock.Lock() _, ok := this.userFreeSpins[userId] if !ok { this.userFreeSpins[userId] = newFreeSpinInfo(userId, this.freeSlotCount, this.WinShapes, this.Wins, this.slotmgr, this.FreeSlotChanged) } this.lock.Unlock() } func (this *GameLogic) getSlotConfig(userId int) string { // update user's ping // ... this.initUserFreeSpin(userId) d, _ := json.Marshal(this.GameLogic_config) return string(d) } func (this *GameLogic) dump(param1, param2 string) { } func (sm *GameLogic) checkRemoveUser() { time.AfterFunc(slotcommon.DELAY_CHECK*time.Second, sm.checkRemoveUser) var toRemove []int latestRemoveTime := time.Now().Unix() - slotcommon.DELAY_REMOVE sm.lockRemove.RLock() for k, v := range sm.removingFreespins { if v < latestRemoveTime { toRemove = append(toRemove, k) } } sm.lockRemove.RUnlock() if len(toRemove) == 0 { return } sm.lockRemove.Lock() for _, v := range toRemove { delete(sm.removingFreespins, v) } sm.lockRemove.Unlock() sm.lock.Lock() for _, v := range toRemove { delete(sm.userFreeSpins, v) } sm.lock.Unlock() } func (this *GameLogic) test() { if len(os.Args) < 2 { return } if os.Args[1] != "panda" { return } init := 1000000000 if len(os.Args) >= 3 { init, _ = strconv.Atoi(os.Args[2]) } initGold := init betAmount := 250000 if len(os.Args) >= 4 { betAmount, _ = strconv.Atoi(os.Args[3]) } maxCount := 1000 if len(os.Args) >= 5 { maxCount, _ = strconv.Atoi(os.Args[4]) } log.Debug("testing panda slot initGold[%d] betAmount[%d] maxCount[%d]", initGold, betAmount, maxCount) testId := 0 freeWin := 0 bonusWin := 0 jackpotWin := 0 bonusCount := 0 this.userFreeSpins[testId] = newFreeSpinInfo(testId, this.freeSlotCount, this.WinShapes, this.Wins, this.slotmgr, this.FreeSlotChanged) resultSet := make([]SlotPanda_Result, maxCount) var wg sync.WaitGroup for i := 0; i < maxCount; i++ { wg.Add(1) go func(idx int) { defer wg.Done() r, _ := this.getResult(testId, betAmount, 0, false) resultSet[idx] = r }(i) } wg.Wait() log.Color(LogColor, "测试结果已准备") for i := 0; i < maxCount; i++ { free, freeResult, _ := this.useFreeSpin(testId) if free { i-- freeWin += freeResult.getWinAmount() initGold += freeResult.getWinAmount() continue } if initGold < betAmount { break } initGold -= betAmount result := resultSet[i] bonusAmount := 0 if result.Bonus != nil { bonusCount++ bonusAmount = result.Bonus.BonusResult + result.Bonus.JackpotResult bonusWin += result.Bonus.BonusResult jackpotWin += result.Bonus.JackpotResult } initGold += result.WinAmount + bonusAmount } log.Debug("pandaslot left[%d],win[%d],BonusCount[%d],Bonus[%d],Jackpot[%d],Free[%d]", initGold, initGold-init, bonusCount, bonusWin, jackpotWin, freeWin) } func (this *GameLogic) refreshData() { go this.initData() time.AfterFunc(5*time.Minute, this.refreshData) } func (this *GameLogic) initData() { data, err := os.ReadFile("slotconf/slotpanda.json") if err != nil { log.Error("read slotpanda.json failed") } this.lock.Lock() err = json.Unmarshal(data, &this) if err != nil { log.Error("Unmarshal slotpanda.json failed err:%v", err) this.lock.Unlock() return } this.slotmgr = newSlotManager(this.Slots) this.slotCounts = slotcount.NewMultipleSlotCountManager("slotpanda_normal_mul") // newSlotCounts(this.SlotCounts) this.bonusSlotCount = slotcount.NewMultipleSlotCountManager("slotpanda_bonus_mul") //newSlotCounts(this.BonusSlotCounts) this.freeSlotCount = slotcount.NewMultipleSlotCountManager("slotpanda_free_mul") //newSlotCounts(this.FreeSlotCounts) this.betLevelManager = betlevel.NewBetLevelManagerByData(this.BetLevels) this.jackpotManager = newJackpotManager(this.slotmgr.getBonusSlotId(), this.MinJackpotGameCount, this.bonusSlotCount, this.betLevelManager) this.lock.Unlock() if len(this.Test) > 0 { log.Debug("slotpanda testing") result, _ := this.getResult(0, 100, 0, false) d, _ := json.Marshal(result) log.Debug("%s", string(d)) } } func (this *GameLogic) getUserSlotCountNormal(userId int, betAmount int) *slotcount.SlotCountManager { level := 0 return this.slotCounts.GetMgr(level) } func (this *GameLogic) get15Slots(betAmount int, userId int) []Slot { // 如果有测试 if len(this.Test) > 0 { count := len(this.Test) log.Debug("test count = %d", count) ret := make([]Slot, 15) slotIDs := this.Test[rand.Intn(len(this.Test))].Slots if len(slotIDs) < 15 { for i := 0; i < 15-len(slotIDs); i++ { slotIDs = append(slotIDs, 1+rand.Intn(10)) } } log.Release("slotpanda get15Slots 测试中 %v", slotIDs) for i := 0; i < 15; i++ { ret[i] = this.slotmgr.getSlot(slotIDs[i]) } return ret } betLevel := this.betLevelManager.GetLevel(betAmount) sc := this.getUserSlotCountNormal(userId, betAmount) ret := make([]Slot, RESULT_COUNT) slotIDs := sc.Get15Slots(betLevel) //this.slotCounts.get15Slots(isFree, level) for i := 0; i < 15; i++ { ret[i] = this.slotmgr.getSlot(slotIDs[i]) } return ret } func (this *GameLogic) getOneResult(slotID, slotCount, shapeID int) *Result { for _, v := range this.Wins { if v.SlotID != slotID { continue } for _, rate := range v.Rates { if rate.Count == slotCount { return &Result{SlotID: slotID, SlotCount: slotCount, WinShapeID: shapeID, WinRate: rate.Win} } } } return nil } func (this *GameLogic) getResult(userId int, betAmount int, controlType int, isChipRoom bool) (SlotPanda_Result, bool) { var ret SlotPanda_Result if betAmount <= 0 { log.Release("slotpanda.GameLogic.GetResult betAmount = %d", betAmount) return ret, true } ret.Slots = make([]int, RESULT_COUNT) ret.BetAmount = betAmount // 取15个result slots := this.get15Slots(betAmount, userId) for k, v := range slots { ret.Slots[k] = v.SlotID if v.SlotID == this.slotmgr.getBonusSlotId() { ret.Slots[k] = this.jackpotManager.getRandomMultiple()*100 + v.SlotID } } // 计算结果 shapeCount := len(this.WinShapes) for k, v := range this.WinShapes { // 查看每条连线的数量 slotID, slotCount, _ := v.getCount(slots) // 查看结果 result := this.getOneResult(slotID, slotCount, k) if result != nil { // 中奖了 ret.WinAmount += betAmount * result.WinRate / shapeCount ret.Lines = append(ret.Lines, *result) } } if controlType == 1 && (ret.WinAmount > betAmount || this.jackpotManager.isJackpot(ret.Slots)) { return this.getResult(userId, betAmount, controlType, isChipRoom) } if controlType == 2 && ret.WinAmount < betAmount && !this.jackpotManager.isJackpot(ret.Slots) { return this.getResult(userId, betAmount, controlType, isChipRoom) } // Bonus _, bonus := this.jackpotManager.addJackpot(ret.Slots, betAmount, userId, isChipRoom) ret.Bonus = bonus // 已经产生bonus,不产生freespin if bonus != nil { return ret, true } // 检查是否产生freespin this.lock.RLock() _, ok := this.userFreeSpins[userId] if !ok { log.Debug("Not Exist User:%d", userId) this.lock.RUnlock() return ret, false } ret.FreeSpinInner, ret.FreeSpinOuter = this.userFreeSpins[userId].initFree(betAmount, ret.Slots, isChipRoom) this.lock.RUnlock() if controlType == 1 && ret.FreeSpinInner > 0 { // 控制的时候,需要把已产生的免费次数去掉 log.Release("control and reset free") this.userFreeSpins[userId].removeFreeTimes(ret.FreeSpinInner * ret.FreeSpinOuter) return this.getResult(userId, betAmount, controlType, isChipRoom) } return ret, true } func (this *GameLogic) getSlots() []Slot { this.lock.RLock() defer this.lock.RUnlock() return this.Slots } func (this *GameLogic) getWinShapes() []WinShape { this.lock.RLock() defer this.lock.RUnlock() return this.WinShapes } func (this *GameLogic) useFreeSpin(userId int) (bool, FreeSpinResult, bool) { this.lock.Lock() defer this.lock.Unlock() t, ok := this.userFreeSpins[userId] if !ok { return false, FreeSpinResult{}, false } return t.useFreeSpin() } func (this *GameLogic) getFreeSpinTime(userId int) int { this.lock.RLock() defer this.lock.RUnlock() t, ok := this.userFreeSpins[userId] if !ok { return 0 } return t.getFreeCount() } func (this *GameLogic) getResultDesc(result SlotPanda_Result) string { var total struct { Slots []int Lines []int } total.Slots = result.Slots for _, v := range result.Lines { total.Lines = append(total.Lines, v.WinShapeID) } //total.Special = result.BetAmount * (result.Special.WinRate1 + result.Special.WinRate2) data, _ := json.Marshal(total) return string(data) }