上篇博文写过关于匹配的实现算法:[go游戏开发实践]游戏匹配机制实现
文中并没有对该算法进行优化并测试,没有验证该算法的可用性。
在测试的过程中发现了一些问题,并做出了优化的方案。
这边还是贴一下整理后的关于匹配需要的结构代码
主要包括3个结构:匹配参数、匹配玩家信息、匹配池
package main
import (
"time"
)
//============================匹配参数==========================
type MatchParamModel struct {
SavePoolCheckTime uint32 //蓄池检测时间
SavePoolTime uint32 //蓄池时间
SavePoolNum uint32 //蓄池目标数量
MinCount uint32 //队伍最少人数
Count uint32 //队伍人数
MaxWaitTime uint32 //最长等待时间
minRangeParam1 uint32 //参数1的最小值
maxRangeParam1 uint32 //参数1的最大值
minRangeParam2 uint32 //参数2的最小值
maxRangeParam2 uint32 //参数2的最大值
}
//========================匹配玩家信息=========================
type MatchPlayerInfo struct {
playerId uint64 //玩家id
param1 uint32 //玩家参数1
param2 uint32 //玩家参数2
startTime int64 //玩家开始匹配时间
}
func newMatchPlayerInfo(pId uint64, p1 uint32, p2 uint32) *MatchPlayerInfo {
newMatchPlayer := &MatchPlayerInfo{}
newMatchPlayer.param1 = p1
newMatchPlayer.param2 = p2
newMatchPlayer.playerId = pId
newMatchPlayer.startTime = time.Now().Unix()
return newMatchPlayer
}
func (this *MatchPlayerInfo) waitTime() uint64 {
var res uint64
if time.Now().Unix() >= this.startTime {
res = uint64(time.Now().Unix() - this.startTime)
}
return res
}
//============================匹配池================================
type MatchPool struct {
playerArr map[uint64]*MatchPlayerInfo //玩家map
size uint32 //匹配池玩家数量
maxWaitPlayer *MatchPlayerInfo //最长等待玩家
}
func newMatchPool() *MatchPool {
newPool := &MatchPool{}
newPool.playerArr = make(map[uint64]*MatchPlayerInfo)
newPool.size = 0
return newPool
}
func (this *MatchPool) putPlayerIntoPool(pId uint64, p1 uint32, p2 uint32) bool {
newPlayer := newMatchPlayerInfo(pId, p1, p2)
_, ok := this.playerArr[pId]
if ok {
return false
}
this.playerArr[pId] = newPlayer
this.size++
return true
}
func (this *MatchPool) putPlayerArrIntoPool(pArr map[uint64]*MatchPlayerInfo) bool {
res := true
for _, p := range pArr {
res = res && this.putPlayerIntoPool(p.playerId, p.param1, p.param2)
}
return res
}
func (this *MatchPool) removePlayerOutPool(pId uint64) bool {
_, ok := this.playerArr[pId]
if ok && this.size > 0 {
delete(this.playerArr, pId)
this.size--
return true
} else {
return false
}
}
func (this *MatchPool) removePlayerArrOutPool(pArr map[uint64]*MatchPlayerInfo) bool {
res := true
for _, p := range pArr {
res = res && this.removePlayerOutPool(p.playerId)
}
return res
}
func (this *MatchPool) selectPlayerByCount(count uint32, res map[uint64]*MatchPlayerInfo) {
for _, p := range this.playerArr {
res[p.playerId] = p
this.removePlayerOutPool(p.playerId)
count--
if count <= 0 {
break
}
}
}
func (this *MatchPool) selectPlayerByParam(p1 uint32, p2 uint32, count uint32, res map[uint64]*MatchPlayerInfo) {
times := 1
for times <= 3 && count > 0 {
for _, p := range this.playerArr {
flag := true
if times == 1 {
flag = p1 == p.param1 && p2 == p.param2
} else if times == 2 {
flag = p1 == p.param1
}
if flag {
res[p.playerId] = p
this.removePlayerOutPool(p.playerId)
count--
}
if count <= 0 {
break
}
}
times++
}
}
func (this *MatchPool) selectPlayerByWaitTime(waittime uint64, res map[uint64]*MatchPlayerInfo) {
for _, p := range this.playerArr {
if p.waitTime() >= waittime {
res[p.playerId] = p
this.removePlayerOutPool(p.playerId)
}
}
}
func (this *MatchPool) refreshMaxWait() {
var maxwait uint64
for _, p := range this.playerArr {
if maxwait <= p.waitTime() {
maxwait = p.waitTime()
this.maxWaitPlayer = p
}
}
}
下面使用协程测试一下匹配逻辑,看一下会有什么问题:
package main
import (
"fmt"
"math/rand"
"time"
)
var pool *MatchPool
var count uint32
var groupVariance []int
var model *MatchParamModel
func main() {
setModel(2, 4, 50, 19)
pool = newMatchPool()
test1() //~协程测试
time.Sleep(20 * time.Second)
fmt.Println("匹配结束!")
var s int
for _, v := range groupVariance {
s += v
}
fmt.Printf("匹配平均方差为%v", float64(s)/float64(count))
}
//~设置参数
func setModel(spCheckTime uint32, spTime uint32, spNum uint32, mWaitTime uint32) {
model = &MatchParamModel{
SavePoolCheckTime: spCheckTime,
SavePoolTime: spTime,
SavePoolNum: spNum,
MinCount: 8,
Count: 12,
MaxWaitTime: mWaitTime,
minRangeParam1: 1,
maxRangeParam1: 10,
minRangeParam2: 10,
maxRangeParam2: 20,
}
}
func test1() {
var t uint64
for t = 0; t < 5; t++ {
//~随机产生玩家信息 -- 每隔2秒产生100个玩家
var i uint64
fmt.Println("===================开始匹配=============")
m := make(map[uint64]*MatchPlayerInfo)
for i = 0; i < 100; i++ {
p1 := rand.Uint32()%10 + 1
p2 := rand.Uint32()%10 + 11
p := newMatchPlayerInfo(t*1000+i, p1, p2)
if pool.size == 0 { //池子没有玩家,起协程
pool.putPlayerIntoPool(p.playerId, p.param1, p.param2)
go matchProcess(model)
} else {
pool.putPlayerIntoPool(p.playerId, p.param1, p.param2)
}
}
time.Sleep(2 * time.Second)
}
}
//~匹配流程
func matchProcess(model *MatchParamModel) {
if model == nil {
return
}
waitLongPool := make(map[uint64]*MatchPlayerInfo)
//匹配池中有人就持续执行
for pool.size > 0 {
//删除超时玩家
outTimePlayer := make(map[uint64]*MatchPlayerInfo)
pool.selectPlayerByWaitTime(uint64(model.MaxWaitTime), outTimePlayer) //选择的时候已经将玩家从池中移除
if len(outTimePlayer) > 0 { //有超时玩家
for _, p := range outTimePlayer {
//超时通知处理
fmt.Printf("玩家%v匹配超时\n", p.playerId)
}
continue
}
//刷新最长等待玩家 -- 用于最先匹配
pool.refreshMaxWait()
if pool.maxWaitPlayer == nil {
return
}
//蓄池等待 -- 是否达到蓄池目标 玩家等待时间是否超出蓄池时间
if pool.size < model.SavePoolNum && pool.maxWaitPlayer.waitTime() < uint64(model.SavePoolTime) {
//~等待一轮检测时间
time.Sleep(time.Duration(model.SavePoolCheckTime) * time.Second)
continue
}
//进行匹配
for pool.size >= model.MinCount {
//挑选出超出蓄池时间的玩家
pool.selectPlayerByWaitTime(uint64(model.SavePoolTime), waitLongPool)
pool.refreshMaxWait()
//优先匹配长等待玩家
matchPlayer := make(map[uint64]*MatchPlayerInfo)
if len(waitLongPool) > 0 {
for _, p := range waitLongPool {
matchPlayer[p.playerId] = p
delete(waitLongPool, p.playerId)
if uint32(len(matchPlayer)) == model.Count {
break
}
}
}
//正常匹配
if uint32(len(matchPlayer)) <= model.Count {
p1 := pool.maxWaitPlayer.param1
p2 := pool.maxWaitPlayer.param2
needCount := model.Count - uint32(len(matchPlayer))
pool.selectPlayerByParam(p1, p2, needCount, matchPlayer)
if uint32(len(matchPlayer)) >= model.MinCount {
//进行游戏处理
fmt.Println("进行游戏")
var arr []int
for _, p := range matchPlayer {
fmt.Printf("该组玩家为%v \n", p)
arr = append(arr, int(p.param1))
}
fmt.Printf("该组玩家的方差为%v \n", cal(arr))
groupVariance = append(groupVariance, cal(arr))
count++
} else {
//玩家过少 继续放入池中等下一轮蓄池
pool.putPlayerArrIntoPool(matchPlayer)
}
}
pool.putPlayerArrIntoPool(waitLongPool)
}
}
fmt.Printf("一共匹配%v组\n", count)
fmt.Println("====================匹配完毕=======================")
}
//~简单计算方差
func cal(arr []int) int {
var sum int
for _, v := range arr {
sum += v
}
avg := sum / len(arr)
var res int
for _, v := range arr {
res += getPower(v, avg)
}
res = res / len(arr)
return res
}
func getPower(v int, avg int) int {
return (v - avg) * (v - avg)
}
测试会出现:
fatal error: concurrent map iteration and map write
go的map不支持同时的遍历跟读写,所以在协程运行时我对pool中的playerArr进行了遍历,但是在主线程,执行了putPlayerArrIntoPool操作,对map进行了写入
那么go的map为什么会不支持同时的遍历跟读写呢?
深度map分析可以参考:golang-map分析
基于map的不支持并发写,可以对读写操作进行加锁,对pool结构进行一下修改(仅贴出修改的方法跟结构):
type MatchPool struct {
playerArr map[uint64]*MatchPlayerInfo
size uint32
maxWaitPlayer *MatchPlayerInfo
rwMutex sync.RWMutex //加读写锁
}
func (this *MatchPool) putPlayerIntoPool(pId uint64, p1 uint32, p2 uint32) bool {
this.rwMutex.Lock()
defer this.rwMutex.Unlock()
newPlayer := newMatchPlayerInfo(pId, p1, p2)
_, ok := this.playerArr[pId]
if ok {
return false
}
this.playerArr[pId] = newPlayer
this.size++
return true
}
func (this *MatchPool) removePlayerArrOutPool(pArr map[uint64]*MatchPlayerInfo) bool {
this.rwMutex.Lock()
defer this.rwMutex.Unlock()
res := true
for _, p := range pArr {
res = res && this.removePlayerOutPool(p.playerId)
}
return res
}
func (this *MatchPool) selectPlayerByCount(count uint32, res map[uint64]*MatchPlayerInfo) {
this.rwMutex.Lock()
defer this.rwMutex.Unlock()
for _, p := range this.playerArr {
res[p.playerId] = p
this.removePlayerOutPool(p.playerId)
count--
if count <= 0 {
break
}
}
}
func (this *MatchPool) selectPlayerByParam(p1 uint32, p2 uint32, count uint32, res map[uint64]*MatchPlayerInfo) {
this.rwMutex.Lock()
defer this.rwMutex.Unlock()
times := 1
for times <= 3 && count > 0 {
for _, p := range this.playerArr {
flag := true
if times == 1 {
flag = p1 == p.param1 && p2 == p.param2
} else if times == 2 {
flag = p1 == p.param1
}
if flag {
res[p.playerId] = p
this.removePlayerOutPool(p.playerId)
count--
}
if count <= 0 {
break
}
}
times++
}
}
func (this *MatchPool) selectPlayerByWaitTime(waittime uint64, res map[uint64]*MatchPlayerInfo) {
this.rwMutex.Lock()
defer this.rwMutex.Unlock()
for _, p := range this.playerArr {
if p.waitTime() >= waittime {
res[p.playerId] = p
this.removePlayerOutPool(p.playerId)
}
}
}
func (this *MatchPool) refreshMaxWait() {
this.rwMutex.Lock()
defer this.rwMutex.Unlock()
var maxwait uint64
for _, p := range this.playerArr {
if maxwait <= p.waitTime() {
maxwait = p.waitTime()
this.maxWaitPlayer = p
}
}
}
加了锁之后,可以测试可以正确执行,这里就不贴测试结果了。
下面进行进一步的优化:
var p chan *MatchPlayerInfo
var start chan int
var quit chan int
func main() {
setModel(2, 4, 200, 19)
pool = newMatchPool()
test2()
time.Sleep(20 * time.Second)
quit <- 1
fmt.Println("===================匹配结束================")
var s int
for _, v := range groupVariance {
s += v
}
fmt.Printf("匹配平均方差为%v", float64(s)/float64(count))
}
func test2() {
p = make(chan *MatchPlayerInfo, 100) //玩家通道
start = make(chan int, 1) //匹配信号
quit = make(chan int, 1) //退出信号
go doRun()
go genPlayer()
go clockStart()
}
func doRun() {
for {
select {
case play := <-p:
pool.putPlayerIntoPool(play.playerId, play.param1, play.param2)
case <-start:
matchProcess2(model)
case <-quit:
return
}
}
}
func matchProcess2(model *MatchParamModel) {
if model == nil {
return
}
fmt.Println("===================开始匹配=======================")
waitLongPool := make(map[uint64]*MatchPlayerInfo)
if pool.size > 0 {
//删除超时玩家
outTimePlayer := make(map[uint64]*MatchPlayerInfo)
pool.selectPlayerByWaitTime(uint64(model.MaxWaitTime), outTimePlayer) //选择的时候已经将玩家从池中移除
if len(outTimePlayer) > 0 { //有超时玩家
for _, p := range outTimePlayer {
//超时通知处理
fmt.Printf("玩家%v匹配超时\n", p.playerId)
}
}
//刷新最长等待玩家 -- 用于最先匹配
pool.refreshMaxWait()
if pool.maxWaitPlayer == nil {
return
}
//蓄池等待 -- 是否达到蓄池目标 玩家等待时间是否超出蓄池时间
if pool.size < model.SavePoolNum && pool.maxWaitPlayer.waitTime() < uint64(model.SavePoolTime) {
return
}
//进行匹配
for pool.size >= model.MinCount {
//挑选出超出蓄池时间的玩家
pool.selectPlayerByWaitTime(uint64(model.SavePoolTime), waitLongPool)
pool.refreshMaxWait()
//匹配超时等待玩家
//正常匹配
matchPlayer := make(map[uint64]*MatchPlayerInfo)
if len(waitLongPool) > 0 {
for _, p := range waitLongPool {
matchPlayer[p.playerId] = p
delete(waitLongPool, p.playerId)
if uint32(len(matchPlayer)) == model.Count {
break
}
}
}
if uint32(len(matchPlayer)) <= model.Count {
p1 := pool.maxWaitPlayer.param1
p2 := pool.maxWaitPlayer.param2
needCount := model.Count - uint32(len(matchPlayer))
pool.selectPlayerByParam(p1, p2, needCount, matchPlayer)
if uint32(len(matchPlayer)) >= model.MinCount && uint32(len(matchPlayer)) <= model.Count {
//进行游戏处理
fmt.Println("进行游戏")
var arr []int
for _, p := range matchPlayer {
fmt.Printf("该组玩家为%v \n", p)
arr = append(arr, int(p.param1))
}
fmt.Printf("该组玩家的方差为%v \n", cal(arr))
groupVariance = append(groupVariance, cal(arr))
count++
} else {
//玩家过少 继续放入池中等下一轮蓄池
pool.putPlayerArrIntoPool(matchPlayer)
}
}
pool.putPlayerArrIntoPool(waitLongPool)
}
}
fmt.Printf("一共匹配%v组\n", count)
fmt.Println("===================匹配完毕========================")
}
func clockStart() {
for i := 0; i < 6; i++ {
start <- 1
time.Sleep(2 * time.Second)
}
}
func genPlayer() {
var t uint64
for t = 0; t < 10; t++ {
//~随机产生玩家信息
var i uint64
for i = 0; i < 50; i++ {
p1 := rand.Uint32()%10 + 1
p2 := rand.Uint32()%10 + 11
p <- newMatchPlayerInfo(t*1000+i, p1, p2)
time.Sleep(time.Millisecond)
}
time.Sleep(2 * time.Second)
}
}
select 语句使一个 Go 程可以等待多个通信操作。
select 会阻塞到某个分支可以继续执行为止,这时就会执行该分支。当多个分支都准备好时会随机选择一个执行。
利用了select将写入pool的操作与匹配遍历操作隔离,使得在同一时间只能有一个操作在执行,同时修改了匹配流程,将匹配过程改为每隔一个clock时间匹配执行一次。
可以设置不同的匹配参数进行测试,下面是上述代码中设置的参数的测试结果:
进行游戏
该组玩家为&{1 5 19 1590560488}
该组玩家为&{16 5 14 1590560488}
该组玩家为&{24 10 17 1590560488}
该组玩家为&{12 5 15 1590560488}
该组玩家为&{37 5 19 1590560488}
该组玩家为&{48 5 17 1590560488}
该组玩家为&{38 5 17 1590560488}
该组玩家为&{8 3 12 1590560488}
该组玩家为&{41 5 18 1590560488}
该组玩家为&{9 5 14 1590560488}
该组玩家为&{25 5 13 1590560488}
该组玩家为&{18 5 14 1590560488}
该组玩家的方差为2
进行游戏
该组玩家为&{35 7 13 1590560488}
该组玩家为&{46 4 15 1590560488}
该组玩家为&{7 7 20 1590560488}
该组玩家为&{45 8 13 1590560488}
该组玩家为&{23 8 19 1590560488}
该组玩家为&{11 8 17 1590560488}
该组玩家为&{43 8 17 1590560488}
该组玩家为&{31 1 14 1590560488}
该组玩家为&{15 4 18 1590560488}
该组玩家为&{34 8 13 1590560488}
该组玩家为&{49 2 18 1590560488}
该组玩家为&{10 1 14 1590560488}
该组玩家的方差为8
进行游戏
该组玩家为&{3 1 11 1590560488}
该组玩家为&{29 7 11 1590560488}
该组玩家为&{27 2 11 1590560488}
该组玩家为&{47 2 18 1590560488}
该组玩家为&{21 10 18 1590560488}
该组玩家为&{28 7 18 1590560488}
该组玩家为&{33 3 16 1590560488}
该组玩家为&{5 10 14 1590560488}
该组玩家为&{4 3 11 1590560488}
该组玩家为&{42 3 20 1590560488}
该组玩家为&{20 7 12 1590560488}
该组玩家为&{30 3 12 1590560488}
该组玩家的方差为10
进行游戏
该组玩家为&{36 6 17 1590560488}
该组玩家为&{6 6 20 1590560488}
该组玩家为&{22 7 16 1590560488}
该组玩家为&{26 9 19 1590560488}
该组玩家为&{0 3 15 1590560488}
该组玩家为&{2 3 17 1590560488}
该组玩家为&{32 4 18 1590560488}
该组玩家为&{19 1 14 1590560488}
该组玩家为&{39 3 11 1590560488}
该组玩家为&{17 9 14 1590560488}
该组玩家为&{13 6 17 1590560488}
该组玩家为&{14 1 12 1590560488}
该组玩家的方差为7
一轮玩家全部匹配完毕!
一共匹配4组
开始匹配进行游戏
该组玩家为&{40 7 17 1590560488}
该组玩家为&{1008 5 15 1590560490}
该组玩家为&{1029 5 17 1590560490}
该组玩家为&{1030 5 13 1590560490}
该组玩家为&{1038 5 11 1590560490}
该组玩家为&{1045 2 15 1590560490}
该组玩家为&{44 4 16 1590560488}
该组玩家为&{1021 5 16 1590560490}
该组玩家为&{1032 5 17 1590560490}
该组玩家为&{1017 5 19 1590560490}
该组玩家为&{1002 5 11 1590560490}
该组玩家为&{1033 5 18 1590560490}
该组玩家的方差为1
进行游戏
该组玩家为&{1011 3 20 1590560490}
该组玩家为&{1005 7 18 1590560490}
该组玩家为&{1031 7 19 1590560490}
该组玩家为&{1004 1 15 1590560490}
该组玩家为&{1003 1 12 1590560490}
该组玩家为&{1028 3 11 1590560490}
该组玩家为&{1023 1 13 1590560490}
该组玩家为&{1024 3 14 1590560490}
该组玩家为&{1034 7 18 1590560490}
该组玩家为&{1018 7 17 1590560490}
该组玩家为&{1047 7 13 1590560490}
该组玩家为&{1037 9 15 1590560490}
该组玩家的方差为8
进行游戏
该组玩家为&{1027 2 18 1590560490}
该组玩家为&{1012 4 11 1590560490}
该组玩家为&{1013 8 14 1590560490}
该组玩家为&{1022 4 20 1590560490}
该组玩家为&{1039 3 19 1590560490}
该组玩家为&{1035 3 11 1590560490}
该组玩家为&{1000 2 14 1590560490}
该组玩家为&{1016 1 12 1590560490}
该组玩家为&{1019 10 17 1590560490}
该组玩家为&{1009 3 19 1590560490}
该组玩家为&{1006 3 15 1590560490}
该组玩家为&{1010 3 15 1590560490}
该组玩家的方差为6
进行游戏
该组玩家为&{1044 6 12 1590560490}
该组玩家为&{1036 1 12 1590560490}
该组玩家为&{1043 1 19 1590560490}
该组玩家为&{1048 1 14 1590560490}
该组玩家为&{1025 1 13 1590560490}
该组玩家为&{1007 9 16 1590560490}
该组玩家为&{1041 2 20 1590560490}
该组玩家为&{1026 9 20 1590560490}
该组玩家为&{1040 1 18 1590560490}
该组玩家为&{1042 2 18 1590560490}
该组玩家为&{1046 9 11 1590560490}
该组玩家为&{1014 9 13 1590560490}
该组玩家的方差为13
一轮玩家全部匹配完毕!
一共匹配8
开始匹配进行游戏
该组玩家为&{2045 9 14 1590560492}
该组玩家为&{2030 9 18 1590560492}
该组玩家为&{2026 9 12 1590560492}
该组玩家为&{2017 4 11 1590560492}
该组玩家为&{1015 6 12 1590560490}
该组玩家为&{1001 2 16 1590560490}
该组玩家为&{1049 2 13 1590560490}
该组玩家为&{2033 9 19 1590560492}
该组玩家为&{2027 9 19 1590560492}
该组玩家为&{1020 9 16 1590560490}
该组玩家为&{2037 9 16 1590560492}
该组玩家为&{2049 9 15 1590560492}
该组玩家的方差为7
进行游戏
该组玩家为&{2040 5 11 1590560492}
该组玩家为&{2041 6 13 1590560492}
该组玩家为&{2039 4 18 1590560492}
该组玩家为&{2002 3 12 1590560492}
该组玩家为&{2006 7 17 1590560492}
该组玩家为&{2021 4 15 1590560492}
该组玩家为&{2014 4 15 1590560492}
该组玩家为&{2011 7 12 1590560492}
该组玩家为&{2003 10 15 1590560492}
该组玩家为&{2046 4 12 1590560492}
该组玩家为&{2022 4 12 1590560492}
该组玩家为&{2032 4 12 1590560492}
该组玩家的方差为3
进行游戏
该组玩家为&{2015 2 11 1590560492}
该组玩家为&{2029 2 11 1590560492}
该组玩家为&{2005 8 13 1590560492}
该组玩家为&{2018 1 19 1590560492}
该组玩家为&{2035 7 15 1590560492}
该组玩家为&{2000 10 15 1590560492}
该组玩家为&{2034 2 13 1590560492}
该组玩家为&{2020 2 14 1590560492}
该组玩家为&{2031 2 17 1590560492}
该组玩家为&{2013 2 14 1590560492}
该组玩家为&{2047 3 18 1590560492}
该组玩家为&{2009 5 16 1590560492}
该组玩家的方差为8
进行游戏
该组玩家为&{2023 10 16 1590560492}
该组玩家为&{2044 10 16 1590560492}
该组玩家为&{2038 10 17 1590560492}
该组玩家为&{2019 10 19 1590560492}
该组玩家为&{2042 5 14 1590560492}
该组玩家为&{2008 10 16 1590560492}
该组玩家为&{2025 10 11 1590560492}
该组玩家为&{2036 10 11 1590560492}
该组玩家为&{2016 6 19 1590560492}
该组玩家为&{2043 1 15 1590560492}
该组玩家为&{2024 8 13 1590560492}
该组玩家为&{2012 5 18 1590560492}
该组玩家的方差为9
一轮玩家全部匹配完毕!
一共匹配12组
开始匹配进行游戏
该组玩家为&{2007 8 14 1590560492}
该组玩家为&{2010 1 14 1590560492}
该组玩家为&{2004 6 15 1590560492}
该组玩家为&{3049 3 11 1590560494}
该组玩家为&{3011 3 14 1590560494}
该组玩家为&{3040 3 19 1590560494}
该组玩家为&{2001 3 16 1590560492}
该组玩家为&{2028 1 16 1590560492}
该组玩家为&{2048 8 14 1590560492}
该组玩家为&{3005 3 16 1590560494}
该组玩家为&{3041 3 12 1590560494}
该组玩家为&{3042 3 15 1590560494}
该组玩家的方差为5
进行游戏
该组玩家为&{3037 7 18 1590560494}
该组玩家为&{3008 7 19 1590560494}
该组玩家为&{3027 4 11 1590560494}
该组玩家为&{3015 4 13 1590560494}
该组玩家为&{3013 4 14 1590560494}
该组玩家为&{3029 6 15 1590560494}
该组玩家为&{3033 1 18 1590560494}
该组玩家为&{3007 10 14 1590560494}
该组玩家为&{3035 8 16 1590560494}
该组玩家为&{3000 8 17 1590560494}
该组玩家为&{3009 5 15 1590560494}
该组玩家为&{3017 10 18 1590560494}
该组玩家的方差为6
进行游戏
该组玩家为&{3032 8 14 1590560494}
该组玩家为&{3019 9 17 1590560494}
该组玩家为&{3039 8 18 1590560494}
该组玩家为&{3014 6 16 1590560494}
该组玩家为&{3026 10 14 1590560494}
该组玩家为&{3034 10 16 1590560494}
该组玩家为&{3020 10 13 1590560494}
该组玩家为&{3006 10 18 1590560494}
该组玩家为&{3021 6 17 1590560494}
该组玩家为&{3012 7 18 1590560494}
该组玩家为&{3048 7 18 1590560494}
该组玩家为&{3003 2 19 1590560494}
该组玩家的方差为5
进行游戏
该组玩家为&{3004 3 16 1590560494}
该组玩家为&{3038 6 13 1590560494}
该组玩家为&{3022 1 13 1590560494}
该组玩家为&{3002 9 20 1590560494}
该组玩家为&{3044 9 17 1590560494}
该组玩家为&{3031 7 12 1590560494}
该组玩家为&{3001 8 18 1590560494}
该组玩家为&{3025 2 14 1590560494}
该组玩家为&{3028 1 19 1590560494}
该组玩家为&{3046 5 15 1590560494}
该组玩家为&{3016 1 15 1590560494}
该组玩家为&{3023 7 11 1590560494}
该组玩家的方差为10
进行游戏
该组玩家为&{3036 7 15 1590560494}
该组玩家为&{3043 5 16 1590560494}
该组玩家为&{3045 2 14 1590560494}
该组玩家为&{3018 6 11 1590560494}
该组玩家为&{3024 1 14 1590560494}
该组玩家为&{3030 7 11 1590560494}
该组玩家为&{3047 9 13 1590560494}
该组玩家为&{3010 9 19 1590560494}
该组玩家的方差为8
一轮玩家全部匹配完毕!
一共匹配17组
开始匹配进行游戏
该组玩家为&{4046 1 14 1590560496}
该组玩家为&{4018 1 11 1590560496}
该组玩家为&{4032 1 13 1590560496}
该组玩家为&{4033 6 13 1590560496}
该组玩家为&{4037 7 14 1590560496}
该组玩家为&{4007 2 11 1590560496}
该组玩家为&{4027 1 14 1590560496}
该组玩家为&{4035 1 20 1590560496}
该组玩家为&{4024 1 19 1590560496}
该组玩家为&{4013 1 15 1590560496}
该组玩家为&{4039 1 15 1590560496}
该组玩家为&{4009 1 19 1590560496}
该组玩家的方差为4
进行游戏
该组玩家为&{4028 8 17 1590560496}
该组玩家为&{4019 4 20 1590560496}
该组玩家为&{4014 5 11 1590560496}
该组玩家为&{4008 10 19 1590560496}
该组玩家为&{4021 10 16 1590560496}
该组玩家为&{4003 2 19 1590560496}
该组玩家为&{4005 9 16 1590560496}
该组玩家为&{4006 7 20 1590560496}
该组玩家为&{4040 8 20 1590560496}
该组玩家为&{4017 10 16 1590560496}
该组玩家为&{4000 5 12 1590560496}
该组玩家为&{4023 2 12 1590560496}
该组玩家的方差为8
进行游戏
该组玩家为&{4041 10 11 1590560496}
该组玩家为&{4004 10 15 1590560496}
该组玩家为&{4012 10 17 1590560496}
该组玩家为&{4045 8 18 1590560496}
该组玩家为&{4044 3 16 1590560496}
该组玩家为&{4029 10 20 1590560496}
该组玩家为&{4020 10 17 1590560496}
该组玩家为&{4001 10 14 1590560496}
该组玩家为&{4016 8 15 1590560496}
该组玩家为&{4011 4 12 1590560496}
该组玩家为&{4030 9 13 1590560496}
该组玩家为&{4034 9 17 1590560496}
该组玩家的方差为5
进行游戏
该组玩家为&{4002 9 19 1590560496}
该组玩家为&{4049 4 11 1590560496}
该组玩家为&{4025 8 11 1590560496}
该组玩家为&{4031 6 13 1590560496}
该组玩家为&{4010 4 14 1590560496}
该组玩家为&{4022 7 19 1590560496}
该组玩家为&{4048 9 12 1590560496}
该组玩家为&{4026 9 15 1590560496}
该组玩家为&{4038 3 12 1590560496}
该组玩家为&{4042 3 13 1590560496}
该组玩家为&{4015 6 16 1590560496}
该组玩家为&{4047 9 12 1590560496}
该组玩家的方差为5
一轮玩家全部匹配完毕!
一共匹配21组
匹配结束!
匹配平均方差为6.571428571428571