package generate import ( "fmt" "git.huoys.com/oversea/game/coin/iceworld/configs" iwProto "git.huoys.com/oversea/game/coin/iceworld/proto" "git.huoys.com/oversea/game/coin/iceworld/utils" "io" "math/rand" "os" "sort" "strconv" "strings" "sync" "time" ) /** 可配置项 **/ var randomTimes = 20000000 //随机次数 var roundPerGroutine = 20000 //一个协程处理多少次随机循环 var maxKinds = 4 //每个样本最多包含多少种图案 var maxSample = 20 //每个倍数最大样本量 /** 不要动 **/ var icons = []int{0,1,2,3,4,5,6,7,8,9} //图案ID,请勿随意改动 var trLocker = sync.Mutex{} var tempResult = make([]sampleResult,0) var rmLocker = sync.Mutex{} var repeatMap = make(map[string]int) type iconRow struct { Bonus float32 Sample []int } type sampleResult struct { Bonus float32 Sample [][]int } type finalResult struct { Id int Bonus string Sample [][][]int } func main(){ GenerateSample() } func GenerateSample (){ timeStart := time.Now().Unix() //图标转map iconMap := make(map[int]int) for _,icon := range(icons){ iconMap[icon] = 0 } //绑定图案和倍数关系 sampleBonus := make(map[int]configs.CommonIcon) oriConfig := configs.CommonGroups protoIconValue := iwProto.Icon_value for k,v := range(protoIconValue){ if _,ok:= iconMap[int(v)];!ok {continue} iconName := strings.ToUpper(k) value := oriConfig[iconName] sampleBonus[int(v)] = value } //每个图案起一个协程去生成中奖图案 iconChan := make(chan map[int][]iconRow) for i:=0 ; i maxKinds{ continue } if _,ok := iconCombines[len(comb)] ; !ok{ iconCombines[len(comb)] = make([][]int,0) } iconCombines[len(comb)] = append(iconCombines[len(comb)],comb) } } //开始随机循环获取样本 i := 0 j := 0 length := roundPerGroutine //每个协程处理的循环次数 wg := sync.WaitGroup{} for { if i >= randomTimes{ break } //每个协程处理10000次随机次数 wg.Add(1) go randomSample(&wg,result,iconCombines,length) i += length j++ } fmt.Println("已开启",j,"个子线程随机"+strconv.Itoa(randomTimes)+"次获取中奖图案,预计耗时"+ strconv.Itoa(int(randomTimes/10000000*380/60)) +"分钟,请稍后...") wg.Wait() //获取到的结果进行排序及组合,干掉超过最大样本量的样本 fmt.Println("样本生成完毕,正在格式化...") finalResult := formatTempResult(tempResult) fmt.Println("格式化完毕,共获取到",len(finalResult),"个倍数样本") //导出toml文件到当前目录下 fmt.Println("正在导出TOML配置文件...") exportTomlFile(finalResult) //导出倍数信息Excel文件到当前目录下 fmt.Println("正在导出倍数Excel文件...") exportBonusExcel(finalResult) timeEnd := time.Now().Unix() fmt.Println("搞定!总耗时:"+strconv.Itoa(int(timeEnd-timeStart))+"秒") fmt.Println("按任意键退出...") var input string _,_ = fmt.Scanln(&input) os.Exit(0) } func exportBonusExcel(result []finalResult){ fileName := "./bonus.csv" checkAndCreateFile(fileName) file,_:=os.OpenFile(fileName,os.O_WRONLY|os.O_CREATE,0777) defer file.Close() content := "" for _,finalRes := range(result){ content += finalRes.Bonus + "\n" } _,_ = io.WriteString(file, content) } func checkAndCreateFile(fileName string) { checkFileExist := func (filename string) bool { var exist = true if _, err := os.Stat(filename); os.IsNotExist(err) { exist = false } return exist } if checkFileExist(fileName){ _ = os.Remove(fileName) } _,err := os.Create(fileName) if err != nil{ panic("创建TOML文件失败!") } return } func exportTomlFile(result []finalResult){ //创建文件 fileName := "./mainsample.toml" checkAndCreateFile(fileName) file,_:=os.OpenFile(fileName,os.O_WRONLY|os.O_CREATE,0777) defer file.Close() //每1000条样本写入一次 content := "#主玩法普通图案排列样本。\n#!!!因涉及到遍历,倍数必须按照数值递增排列,Id从1开始递增!!!\n#样本的第一个数字代表图案,后面五个数字代表该图案的5列排列值\n" lenR := len(result) for i:=0 ; i= maxSample{ continue } sampleMap[bonusString] = finalResult{ Bonus: bonusString, Sample: append(finalSample.Sample,sample.Sample), } }else{ sampleMap[bonusString] = finalResult{ Bonus: bonusString, Sample: [][][]int{sample.Sample}, } } } //map转slice finalSlice := make([]finalResult,0) for _,value := range(sampleMap){ finalSlice = append(finalSlice,value) } //slice按照bonus升序排序 sort.Slice(finalSlice, func(i, j int) bool { iFloat,_ := strconv.ParseFloat(finalSlice[i].Bonus,32) jFloat,_ := strconv.ParseFloat(finalSlice[j].Bonus,32) return iFloat < jFloat }) //每个slice赋值ID var id int = 1 for k,_ := range(finalSlice){ finalSlice[k].Id = id id ++ } return finalSlice } func randomSample(wg *sync.WaitGroup,result map[int][]iconRow,iconCombines map[int][][]int,times int){ for i:=0 ; i 10 && len(strings.Split(formatBonusString(sr.Bonus),".")) > 1 { return false } //校验每列数量总和是否小于等于7 colSum := [5]int{0,0,0,0,0} for _,sample := range(sr.Sample){ //注意sample是6位,包含图标id for k,v := range(sample){ if k > 0{ colSum[k-1] += v } } } for _,v := range(colSum){ if v > 7{ return false } } //重复性校验 checkRepeat := checkSampleRepeat(sr,comb) if !checkRepeat{ return false } return true } func checkSampleRepeat(sr sampleResult,comb []int) bool { repeatStr := "" repeatStr += strconv.FormatFloat(float64(sr.Bonus),'f', 2, 64) + "|" sort.Slice(comb, func(i, j int) bool {return comb[i] < comb[j]}) repeatStr += fmt.Sprintf("%v",comb) + "|" iconSum := 0 for _,sample := range(sr.Sample){ for k,v := range(sample){ if k > 0{ iconSum += v } } } repeatStr += strconv.Itoa(iconSum) return checkAndAddToRepeatMap(repeatStr) } func checkAndAddToRepeatMap(str string) bool { rmLocker.Lock() defer rmLocker.Unlock() if _,ok := repeatMap[str] ; ok { return false }else{ repeatMap[str] = 0 return true } } func randomOneSample(result map[int][]iconRow,comb []int) (sr sampleResult) { for _,iconId := range(comb){ iconRows := result[iconId] rand.Seed(time.Now().UnixNano()) iconSample := iconRows[rand.Intn(len(iconRows))] sr.Bonus += iconSample.Bonus sr.Sample = append(sr.Sample,iconSample.Sample) } return } func appendToTempResult(sampleResult sampleResult){ trLocker.Lock() defer trLocker.Unlock() tempResult = append(tempResult,sampleResult) } func generateIconSample(iconChan chan map[int][]iconRow,icon int, sampleBonus map[string]interface{}){ //生成所有可能性 allKinds := make([][]int,0) for j1:=0 ; j1<8 ; j1++{ for j2:=0 ; j2<8 ; j2++{ for j3:=0 ; j3<8 ; j3++{ for j4:=0 ; j4<8 ; j4++{ for j5:=0 ; j5<8 ; j5++{ allKinds = append(allKinds,[]int{j1,j2,j3,j4,j5}) } } } } } //逐一判断是否中奖,干掉未中奖的,并格式化最终结果 result := make([]iconRow,0) for _,sample := range(allKinds){ //干掉不符合要求的 if sample[0] == 0 || sample[1] == 0 || sample[2] == 0 || (sample[3] == 0 && sample[4] != 0){ continue } //线数统计 lines := sample[0] * sample[1] * sample[2] if sample[3] != 0{lines *= sample[3]} if sample[4] != 0{lines *= sample[4]} //计算倍数值 X := 0 for _,v := range(sample){ if v != 0 { X++ } } times := sampleBonus["X"+strconv.Itoa(X)].(float32) //计算倍数 bonus,_ := strconv.ParseFloat(fmt.Sprintf("%.2f", float32(lines) * times),64) bonusF32 := float32(bonus) result = append(result,iconRow{ Bonus:bonusF32, Sample: append([]int{icon},sample...), }) } res := make(map[int][]iconRow) res[icon] = result iconChan <- res } //组合算法(从nums中取出m个数) func zuheResult(n int, m int) [][]int { if m < 1 || m > n { fmt.Println("Illegal argument. Param m must between 1 and len(nums).") return [][]int{} } //保存最终结果的数组,总数直接通过数学公式计算 result := make([][]int, 0, mathZuhe(n, m)) //保存每一个组合的索引的数组,1表示选中,0表示未选中 indexs := make([]int, n) for i := 0; i < n; i++ { if i < m { indexs[i] = 1 } else { indexs[i] = 0 } } //第一个结果 result = addTo(result, indexs) for { find := false //每次循环将第一次出现的 1 0 改为 0 1,同时将左侧的1移动到最左侧 for i := 0; i < n-1; i++ { if indexs[i] == 1 && indexs[i+1] == 0 { find = true indexs[i], indexs[i+1] = 0, 1 if i > 1 { moveOneToLeft(indexs[:i]) } result = addTo(result, indexs) break } } //本次循环没有找到 1 0 ,说明已经取到了最后一种情况 if !find { break } } return result } //将ele复制后添加到arr中,返回新的数组 func addTo(arr [][]int, ele []int) [][]int { newEle := make([]int, len(ele)) copy(newEle, ele) arr = append(arr, newEle) return arr } func moveOneToLeft(leftNums []int) { //计算有几个1 sum := 0 for i := 0; i < len(leftNums); i++ { if leftNums[i] == 1 { sum++ } } //将前sum个改为1,之后的改为0 for i := 0; i < len(leftNums); i++ { if i < sum { leftNums[i] = 1 } else { leftNums[i] = 0 } } } //根据索引号数组得到元素数组 func findNumsByIndexs(nums []int, indexs [][]int) [][]int { if len(indexs) == 0 { return [][]int{} } result := make([][]int, len(indexs)) for i, v := range indexs { line := make([]int, 0) for j, v2 := range v { if v2 == 1 { line = append(line, nums[j]) } } result[i] = line } return result } //数学方法计算排列数(从n中取m个数) func mathPailie(n int, m int) int { return jieCheng(n) / jieCheng(n-m) } //数学方法计算组合数(从n中取m个数) func mathZuhe(n int, m int) int { return jieCheng(n) / (jieCheng(n-m) * jieCheng(m)) } //阶乘 func jieCheng(n int) int { result := 1 for i := 2; i <= n; i++ { result *= i } return result }