昨天已經看過我們在實務上可能會遇到的需求,利用多個可能重複範圍的配對池,當作匹配搜尋條件,今天讓我們用實作範例來觀察其效果。
本範例同樣基於官方 demo,藉由更改範例內容重新 build images 後完成。
~ kubectl create namespace open-match-demo
~ kubectl apply -n open-match-demo -f ~/open-match/overlapping.yml
隨機產生不同等級條件 tickets,這段跟之前相同不贅述,請參考 client.go#95
這是產生 overlapping MatchProfile 的部分,不同的 MatchProfile
,以不同的等級上下限表示不同的配對池。以下這些 MatchProfile
會被 Director 向核心詢問,並且呼叫 MMF 執行配對判斷,你可以把它想成像是不同的階級如:
新手場
req := &pb.FetchMatchesRequest{
Config: &pb.FunctionConfig{
Host: "om-function.open-match-demo.svc.cluster.local",
Port: 50502,
Type: pb.FunctionConfig_GRPC,
},
Profile: &pb.MatchProfile{
Name: "MatchProfile1",
Pools: []*pb.Pool{
{
Name: "low level",
DoubleRangeFilters: []*pb.DoubleRangeFilter{
{
DoubleArg: "level",
Max: 10,
Min: 0,
},
},
},
},
},
}
中級場
req := &pb.FetchMatchesRequest{
Config: &pb.FunctionConfig{
Host: "om-function.open-match-demo.svc.cluster.local",
Port: 50502,
Type: pb.FunctionConfig_GRPC,
},
Profile: &pb.MatchProfile{
Name: "MatchProfile2",
Pools: []*pb.Pool{
{
Name: "mid level",
DoubleRangeFilters: []*pb.DoubleRangeFilter{
{
DoubleArg: "level",
Max: 28,
Min: 10,
},
},
},
},
},
}
高手場
req := &pb.FetchMatchesRequest{
Config: &pb.FunctionConfig{
Host: "om-function.open-match-demo.svc.cluster.local",
Port: 50502,
Type: pb.FunctionConfig_GRPC,
},
Profile: &pb.MatchProfile{
Name: "MatchProfile3",
Pools: []*pb.Pool{
{
Name: "high level",
DoubleRangeFilters: []*pb.DoubleRangeFilter{
{
DoubleArg: "level",
Max: 30,
Min: 25,
},
},
},
},
},
}
特此告知 makeMatches()
是直接使用 Skill Based Match Function,我只修改 SearchFields “level” 的部分。說明一下程式內容,與官方 demo 不同的地方在於,建立了 computeQuality() 計算出每場配對的等差,並將這項資訊回填於 Match.Extensions["evaluation_input"]
,這個資訊會被我們建立的 default Evaluator 所採用,pb.DefaultEvaluationCriteria
內記載的 Score,將用來比較最適合的配對組合。
From: https://gist.github.com/syntxerror/c634dccb96c686b50fee67f801d6e671
func makeMatches(p *pb.MatchProfile, poolTickets map[string][]*pb.Ticket) ([]*pb.Match, error) {
// Create a colletion to hold match proposals
var matches []*pb.Match
count := 0
for {
insufficientTickets := false
// Create a collection to hold tickets selected for a match
matchTickets := []*pb.Ticket{}
for pool, tickets := range poolTickets {
// Set flag if there are not enough tickets to create a match
if len(tickets) < ticketsPerPoolPerMatch {
insufficientTickets = true
break
}
// Sort the tickets based on skill
sort.Slice(tickets, func(i, j int) bool {
return tickets[i].SearchFields.DoubleArgs["mmr"] < tickets[j].SearchFields.DoubleArgs["mmr"]
})
// Remove the Tickets from this pool and add to the match proposal.
matchTickets = append(matchTickets, tickets[0:ticketsPerPoolPerMatch]...)
poolTickets[pool] = tickets[ticketsPerPoolPerMatch:]
}
if insufficientTickets {
break
}
// Compute the match quality/score
matchQuality := computeQuality(matchTickets)
evaluationInput, err := ptypes.MarshalAny(&pb.DefaultEvaluationCriteria{
Score: matchQuality,
})
if err != nil {
log.Printf("Failed to marshal DefaultEvaluationCriteria, got %s.", err.Error())
return nil, fmt.Errorf("Failed to marshal DefaultEvaluationCriteria, got %w", err)
}
// Output the match quality for our sanity
log.Printf("Quality for the generated match is %v", matchQuality)
// Create a match proposal
matches = append(matches, &pb.Match{
MatchId: fmt.Sprintf("profile-%v-time-%v-%v", p.GetName(), time.Now().Format("2006-01-02T15:04:05.00"), count),
MatchProfile: p.GetName(),
MatchFunction: matchName,
Tickets: matchTickets,
Extensions: map[string]*any.Any{
"evaluation_input": evaluationInput,
},
})
count++
}
return matches, nil
}
// Compute the quality as a difference in the highest and lowest player skill levels. This can be used to determine if the match is outside a given skill differential
func computeQuality(tickets []*pb.Ticket) float64 {
quality := 0.0
high := 0.0
low := tickets[0].SearchFields.DoubleArgs["mmr"]
for _, ticket := range tickets {
if high < ticket.SearchFields.DoubleArgs["mmr"] {
high = ticket.SearchFields.DoubleArgs["mmr"]
}
if low > ticket.SearchFields.DoubleArgs["mmr"] {
low = ticket.SearchFields.DoubleArgs["mmr"]
}
}
quality = high - low
return quality
}