101 lines
2.7 KiB
Go
101 lines
2.7 KiB
Go
package domain
|
|
|
|
import (
|
|
"fmt"
|
|
"reflect"
|
|
"testing"
|
|
)
|
|
|
|
func TestBalanceProducesDeterministicFullTeamsAndReserve(t *testing.T) {
|
|
var players []Player
|
|
for i := 0; i < 23; i++ {
|
|
players = append(players, Player{
|
|
ID: fmt.Sprintf("p%02d", i),
|
|
Ratings: Ratings{
|
|
Tank: 5 + i%36,
|
|
Damage: 5 + (i*3)%36,
|
|
Support: 5 + (i*7)%36,
|
|
},
|
|
})
|
|
}
|
|
first, err := Balance("event", players)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
second, _ := Balance("event", players)
|
|
if !reflect.DeepEqual(first, second) {
|
|
t.Fatal("balancer output is not deterministic")
|
|
}
|
|
if len(first) != 3 {
|
|
t.Fatalf("got %d candidates", len(first))
|
|
}
|
|
for _, candidate := range first {
|
|
if len(candidate.Teams) != 4 || len(candidate.Reserve) != 3 {
|
|
t.Fatalf("unexpected allocation: %d teams, %d reserve", len(candidate.Teams), len(candidate.Reserve))
|
|
}
|
|
for _, team := range candidate.Teams {
|
|
counts := map[Role]int{}
|
|
for _, slot := range team.Slots {
|
|
counts[slot.Role]++
|
|
}
|
|
if counts[Tank] != 1 || counts[Damage] != 2 || counts[Support] != 2 {
|
|
t.Fatalf("invalid composition: %#v", counts)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestBalanceKeepsOddNumberOfFullTeams(t *testing.T) {
|
|
players := make([]Player, 15)
|
|
for i := range players {
|
|
players[i] = Player{
|
|
ID: fmt.Sprintf("p%02d", i),
|
|
Ratings: Ratings{Tank: 10 + i, Damage: 15 + i%20, Support: 8 + (i*3)%30},
|
|
}
|
|
}
|
|
candidates, err := Balance("event", players)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, candidate := range candidates {
|
|
if len(candidate.Teams) != 3 || len(candidate.Reserve) != 0 {
|
|
t.Fatalf("expected three full teams, got %d teams and %d reserve", len(candidate.Teams), len(candidate.Reserve))
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestBalanceUsesSoftRoleTeammateAndAvoidPreferences(t *testing.T) {
|
|
players := make([]Player, 10)
|
|
for i := range players {
|
|
players[i] = Player{
|
|
ID: fmt.Sprintf("p%02d", i),
|
|
Ratings: Ratings{Tank: 20, Damage: 20, Support: 20},
|
|
}
|
|
}
|
|
players[0].PreferredRoles = []Role{Support}
|
|
players[0].PreferredPlayerIDs = []string{"p01"}
|
|
players[0].AvoidedPlayerIDs = []string{"p02"}
|
|
candidates, err := Balance("event", players)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
best := candidates[0]
|
|
var teamForP0, teamForP1, teamForP2 int = -1, -1, -1
|
|
var roleForP0 Role
|
|
for teamIndex, team := range best.Teams {
|
|
for _, slot := range team.Slots {
|
|
switch slot.PlayerID {
|
|
case "p00":
|
|
teamForP0, roleForP0 = teamIndex, slot.Role
|
|
case "p01":
|
|
teamForP1 = teamIndex
|
|
case "p02":
|
|
teamForP2 = teamIndex
|
|
}
|
|
}
|
|
}
|
|
if teamForP0 != teamForP1 || teamForP0 == teamForP2 || roleForP0 != Support {
|
|
t.Fatalf("preferences were not reflected in best candidate: role=%s teams=%d/%d avoid=%d", roleForP0, teamForP0, teamForP1, teamForP2)
|
|
}
|
|
}
|