package wildapeslot import ( "encoding/json" "fmt" "math/rand" "os" "sort" "strconv" "sync" "time" "bet24.com/log" "bet24.com/servers/games/slotcommon" "bet24.com/servers/games/slotcommon/betlevel" "bet24.com/servers/games/slotcommon/usermanager" "bet24.com/utils" ) const DELAY_REMOVE = 300 // 300秒后删除用户 const DELAY_CHECK = 60 // 每分钟检查一次 type userFreeSpin struct { freeSpinTime int // 免费次数 lastBetAmount int fromAd bool isBonus bool } type GameLogic struct { AdBets []DragonSlot_FreeAdBet TaxRate int BonusFree []int BonusWild []int MinJackpot int MaxNoneFreeCount int ShapeConfig ShapeConfig lock *sync.RWMutex userFreeSpins map[int]*userFreeSpin freeSpin *FreeSpinInfo slotSink slotcommon.SlotSink multipleCountMgr *MultipleSlotCountManager //slotCountsAd *SlotCountManager betLevelManager *betlevel.BetLevelManager slotManager *SlotManager Test []TestSlots testWinAmount int slotCountEasy *SlotCountManager lockRemove *sync.RWMutex removingFreespins map[int]int64 slotCommon *slotcommon.Slotcommon } func NewGameLogic(slotSink slotcommon.SlotSink) *GameLogic { obj := new(GameLogic) obj.lock = &sync.RWMutex{} obj.userFreeSpins = make(map[int]*userFreeSpin) obj.slotSink = slotSink obj.removingFreespins = make(map[int]int64) obj.lockRemove = &sync.RWMutex{} return obj } func (this *GameLogic) run() { log.Color(LogColor, "wildapeslot.GameLogic run") rand.Seed(time.Now().UnixNano()) this.slotCommon = slotcommon.NewSlotCommon(this.slotSink, GAMEID, GAME_NAME, this.TaxRate, GAME_MESSAGE) this.initData() time.AfterFunc(5*time.Minute, this.refreshData) //this.test() go this.checkRemoveUser() } func (sm *GameLogic) checkRemoveUser() { time.AfterFunc(DELAY_CHECK*time.Second, sm.checkRemoveUser) var toRemove []int latestRemoveTime := time.Now().Unix() - 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() { defer utils.TimeCost(fmt.Sprintf("GameLogic.test"))() /* for _, v := range this.betLevelManager.levels { this.testLevel() } */ testIndex, err := strconv.Atoi(os.Args[1]) if err != nil { return } log.Color(LogColor, "wildapeslot.GameLogic test") levels := this.betLevelManager.Levels() v := levels[len(levels)-1-testIndex] this.testLevel(v.Bet) log.Color(LogColor, "wildapeslot.GameLogic test end") } func (this *GameLogic) testLevel(bet int) { m, _ := strconv.Atoi(os.Args[2]) tc, _ := strconv.Atoi(os.Args[3]) originAmount := bet * m log.Debug("开始测试 带入金额[%d] 最多测试次数[%d] 测试底注[%d]", originAmount, tc, bet) winAmount := 0 testAmount := bet totalJackpot := 0 testedCount := 0 //betLevel, base := this.betLevelManager.getLevelAndBase(testAmount) betAmount := 0 returnAmount := 0 taxAmount := 0 for i := 0; i < tc; i++ { if originAmount < bet { log.Debug("break %d < %d", originAmount, bet) break } betAmount += bet originAmount -= bet testedCount++ //slots := this.slotCountManager.get15Slots(betLevel) //log.Color(LogColor, "test %d +++++++++++++++", i) //dumpSlots(slots) //results := this.slotManager.getResults(slots) this.testWinAmount = winAmount result := this.getResult(0, testAmount, false, false) winAmount += result.WinAmount winAmount -= bet if result.WinAmount-bet > 0 { taxAmount += int((result.WinAmount - bet) / 100 * this.TaxRate) } returnAmount = returnAmount + result.WinAmount //tc += result.FreeSpinCount //freeCount += result.FreeSpinCount //originAmount += result.FreeSpinCount * bet originAmount += result.WinAmount //log.Color(LogColor, "test %d end ------------------", i) } log.Color(LogColor, "转数[%d] 底注[%d] 奖池[%d],剩余[%d],输赢[%d] 税[%d] 返还率[%.4f] 概率分布%v", testedCount, bet, totalJackpot, originAmount, winAmount, taxAmount, float64(returnAmount)/float64(betAmount), this.multipleCountMgr.testCount) } func (this *GameLogic) refreshData() { go this.initData() time.AfterFunc(5*time.Minute, this.refreshData) } func (this *GameLogic) initData() { data, err := os.ReadFile("slotconf/wildapeslot_config.json") if err != nil { log.Error("read wildapeslot_config.json failed") } this.lock.Lock() err = json.Unmarshal(data, &this) if err != nil { log.Error("Unmarshal wildapeslot_config.json failed err:%v", err) this.lock.Unlock() return } this.ShapeConfig.loadConfig() this.freeSpin = newFreeSpinInfo() this.multipleCountMgr = newMultipleSlotCountManager() this.betLevelManager = betlevel.NewBetLevelManager("dfdcbetlevel", Msg_BetLevel) this.slotCommon.SetBetLevelManager(this.betLevelManager) this.slotManager = newSlotManager() sort.Slice(this.AdBets, func(i, j int) bool { return this.AdBets[i].AdCount > this.AdBets[j].AdCount }) this.lock.Unlock() if this.MaxNoneFreeCount > 0 { usermanager.SetMaxNoneFreeCount(GAMEID, this.MaxNoneFreeCount) } if len(os.Args) >= 2 { time.AfterFunc(time.Second, this.test) } } func (this *GameLogic) getWinShapes() ShapeConfig { this.lock.RLock() defer this.lock.RUnlock() return this.ShapeConfig } func (this *GameLogic) getUserSlotCount(userId int) *SlotCountManager { return this.multipleCountMgr.getMgr(0) } func (this *GameLogic) getResult(userId int, betAmount int, isFree bool, isBonus bool) DragonSlot_Result { var ret DragonSlot_Result slotCountManager := this.getUserSlotCount(userId) if slotCountManager == nil { log.Release("GameLogic.getResult no slotcount manager") return ret } slotCount := RESULT_COUNT if isFree || isBonus { slotCount = FREE_RESULT_COUNT } ret.Slots = make([]int, slotCount) ret.BetAmount = betAmount level, base := this.betLevelManager.GetLevelAndBase(betAmount) // 取15个result if isFree || isBonus { ret.Slots = this.freeSpin.get15Slots(level, isFree, isBonus) } else { if len(this.Test) > 0 { ret.Slots = this.getTestSlots() } else { ret.Slots = slotCountManager.get15Slots(level, false, false) } } //ret.Lines = this.slotManager.getResults(ret.Slots) log.Debug("new Slots=%v", ret.Slots) this.fillWinLines(ret.Slots, isFree, isBonus, &ret) if len(ret.Lines) > 0 { for _, v := range ret.Lines { winAmount := int(float64(base) * v.WinRate) ret.WinAmount += winAmount } } ret.FreeApe = this.freeSpin.getFreeApe(ret.Slots) newBonus := this.fillBonusResult(ret.Slots, &ret) if ret.FreeApe { log.Debug("has FreeApe") this.addFreeSpin(userId, 3, betAmount, false, false) } else if newBonus { log.Debug("has newBonus") this.addFreeSpin(userId, ret.BonusFree, betAmount, false, true) } return ret } func (this *GameLogic) getTestSlots() []int { // 如果有测试 count := len(this.Test) log.Debug("test count = %d", count) ret := make([]int, 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("wildapeslot getTestSlots 测试中 %v", slotIDs) for i := 0; i < 15; i++ { ret[i] = slotIDs[i] } return ret } //判断修改bonus结果 func (this *GameLogic) fillBonusResult(slots []int, ret *DragonSlot_Result) bool { count := this.slotManager.getBonusCount(slots) if count >= 3 { ret.BonusFree = this.BonusFree[rand.Intn(len(this.BonusFree))] ret.WildCount = this.BonusWild[rand.Intn(len(this.BonusWild))] return true } return false } //判断修改线路结算结果 func (this *GameLogic) fillWinLines(slots []int, isFree bool, isBonus bool, ret *DragonSlot_Result) { var shapeCfgs []WinShape if isFree || isBonus { log.Debug("USE WinShape2 config") shapeCfgs = this.getWinShapes().WinShape2 } else { shapeCfgs = this.getWinShapes().WinShape1 } // 计算结果 shapeCount := len(shapeCfgs) for k, v := range shapeCfgs { // 查看每条连线的数量 slotID, slotCount, _ := v.getCount(slots) // 查看结果 result := this.getOneResult(slotID, slotCount, k) if result != nil { // 中奖了 ret.WinAmount += ret.BetAmount * int(result.WinRate) / shapeCount ret.Lines = append(ret.Lines, *result) } } } //返回结果结构体 func (this *GameLogic) getOneResult(slotID, slotCount, shapeID int) *Result { if slotCount < 3 { return nil } for _, v := range this.slotManager.Slots { if v.SlotID != slotID { continue } var winFactor float64 = 1 if slotCount == 3 { winFactor = v.Win3 } else if slotCount == 4 { winFactor = v.Win4 } else { winFactor = v.Win5 } log.Debug("new Line Slotid=%d shape=%d", slotID, shapeID) return &Result{SlotID: slotID, SlotCount: slotCount, WinShape: shapeID, WinRate: winFactor} } return nil } func (this *GameLogic) useFreeSpin(userId int) (bool, int, bool, bool) { this.lock.Lock() defer this.lock.Unlock() t, ok := this.userFreeSpins[userId] if !ok || t.freeSpinTime <= 0 { return false, 0, false, false } t.freeSpinTime-- log.Debug("免费次数剩余 %d isBonus=%t", t.freeSpinTime, t.isBonus) return t.isBonus, t.lastBetAmount, t.fromAd, t.freeSpinTime >= 0 } 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.freeSpinTime } func (this *GameLogic) userExit(userId int) { this.slotCommon.OnUserExit(userId) if this.getFreeSpinTime(userId) > 0 { this.lockRemove.Lock() this.removingFreespins[userId] = time.Now().Unix() this.lockRemove.Unlock() return } this.lock.Lock() defer this.lock.Unlock() delete(this.userFreeSpins, userId) } func (this *GameLogic) getBetDesc(betAmount int) string { return fmt.Sprintf("bet %d", betAmount) } func (this *GameLogic) getResultDesc(result DragonSlot_Result) string { var total struct { Slots []int Lines []int } total.Slots = result.Slots for _, v := range result.Lines { total.Lines = append(total.Lines, v.WinShape) } data, _ := json.Marshal(total) return string(data) } func (this *GameLogic) addFreeSpin(userId int, freeCount, bet int, fromAd bool, isBonus bool) { this.lock.Lock() this.userFreeSpins[userId] = &userFreeSpin{freeSpinTime: freeCount, lastBetAmount: bet, fromAd: fromAd, isBonus: isBonus} this.lock.Unlock() }