go.charczuk.com@v0.0.0-20240327042549-bc490516bd1a/experiments/matchmaker/main.go (about) 1 /* 2 3 Copyright (c) 2024 - Present. Will Charczuk. All rights reserved. 4 Use of this source code is governed by a MIT license that can be found in the LICENSE file at the root of the repository. 5 6 */ 7 8 package main 9 10 import ( 11 "encoding/csv" 12 "flag" 13 "fmt" 14 "os" 15 "time" 16 17 "go.charczuk.com/experiments/matchmaker/pkg/sim" 18 ) 19 20 func main() { 21 var matchmaker, csvOutput string 22 flag.StringVar(&matchmaker, "matchmaker", "simple", "The matchmaker (simple|balanced|clustered)") 23 flag.StringVar(&csvOutput, "csv-out", "", "The results csv output path (if unset, no csv is written)") 24 flag.Parse() 25 26 s := new(sim.Simulation) 27 28 switch matchmaker { 29 case "simple": 30 s.Matchmaker = new(sim.SimpleMatchmaker) 31 case "balanced": 32 s.Matchmaker = new(sim.BalancedMatchmaker) 33 case "clustered": 34 s.Matchmaker = new(sim.ClusteredMatchmaker) 35 } 36 37 fmt.Printf("Using Matchmaker: %s\n", matchmaker) 38 fmt.Printf("Using Simulation Length: %v\n", s.Config.SimulationLengthOrDefault().Round(time.Second)) 39 fmt.Printf("Using Tick Interval: %v\n", s.Config.TickIntervalOrDefault()) 40 fmt.Printf("Using Server Count: %d\n", s.Config.ServerCountOrDefault()) 41 fmt.Printf("Using Game Team Size: %d\n", s.Config.GameTeamSizeOrDefault()) 42 fmt.Printf("Using Game Duration: %v\n", s.Config.GameDurationOrDefault().Round(time.Second)) 43 44 fmt.Printf("Using Player Active Duration: %v\n", s.Config.PlayerActiveDurationOrDefault()) 45 fmt.Printf("Using Player Natural Rating Median: %0.2f\n", s.Config.PlayerNaturalRatingMedianOrDefault()) 46 fmt.Printf("Using Player Natural Rating StdDev: %0.2f\n", s.Config.PlayerNaturalRatingStdDevOrDefault()) 47 fmt.Printf("Using Player Minimum Rating: %d\n", s.Config.PlayerMinimumRatingOrDefault()) 48 49 s.Init() 50 51 start := time.Now() 52 results := s.Simulate() 53 54 fmt.Println("---") 55 fmt.Printf("Simulation Complete (%v elapsed)\n", time.Since(start)) 56 fmt.Println("---") 57 58 fmt.Printf("Games Played: %v\n", results.GamesPlayed) 59 fmt.Printf("Games Lobsided: %v\n", results.GamesLobsided) 60 fmt.Printf("Games Lobsided Pct: %0.4f%%\n", (float64(results.GamesLobsided)/float64(results.GamesPlayed))*100) 61 fmt.Printf("Games Tied: %v\n", results.GamesTied) 62 63 fmt.Printf("Player Queued Time Mean: %v\n", results.QueueTimeMean.Round(time.Millisecond).String()) 64 fmt.Printf("Player Queued Time P95: %v\n", results.QueueTimeP95.Round(time.Millisecond).String()) 65 66 fmt.Printf("Player Winrate P20: %0.2f\n", results.WinrateP20) 67 fmt.Printf("Player Winrate P50: %0.2f\n", results.WinrateMean) 68 fmt.Printf("Player Winrate P80: %0.2f\n", results.WinrateP80) 69 70 fmt.Printf("Player Skill to Rating Correlation: %0.4f\n", results.PlayerNaturalSkillToRatingCorrelation) 71 72 fmt.Println("---") 73 fmt.Println("Ranks") 74 for _, p := range results.PlayerCountsByRank { 75 if len(p.K) > 7 { 76 fmt.Printf("\t%s\t%v\n", p.K, p.V) 77 } else { 78 fmt.Printf("\t%s\t\t%v\n", p.K, p.V) 79 } 80 } 81 82 fmt.Println("---") 83 fmt.Println("Top 10 players") 84 for _, p := range results.PlayerTop10 { 85 printPlayer(p) 86 } 87 fmt.Println("---") 88 fmt.Println("Bottom 10 players") 89 for _, p := range results.PlayerBottom10 { 90 printPlayer(p) 91 } 92 93 if csvOutput != "" { 94 _ = writePlayersToCSV(csvOutput, results.Players) 95 fmt.Println("---") 96 fmt.Println("wrote results csv to", csvOutput) 97 } 98 } 99 100 func printPlayer(p *sim.Player) { 101 fmt.Printf("\tid=%s games=%v skill=%v rating=%v winrate=%0.2f winrate_recent=%0.2f\n", p.ID.ShortString(), p.Games, p.NaturalSkill, p.Rating, p.Winrate(), p.WinrateRecent()) 102 } 103 104 func writePlayersToCSV(outputPath string, players []*sim.Player) error { 105 f, err := os.Create(outputPath) 106 if err != nil { 107 return err 108 } 109 defer f.Close() 110 111 csvw := csv.NewWriter(f) 112 _ = csvw.Write([]string{ 113 "id", "skill", "rating", "wins", "total", "recent_winrate", 114 "queued_min", "queued_mean", "queued_max", "queued_p50", "queued_p75", "queued_p95", 115 }) 116 for _, p := range players { 117 qmin, qmean, qmax, qp50, qp75, qp95 := p.QueuedElapsedStats() 118 _ = csvw.Write([]string{ 119 p.ID.String(), 120 fmt.Sprint(p.NaturalSkill), 121 fmt.Sprint(p.Rating), 122 fmt.Sprint(p.Wins), 123 fmt.Sprint(p.Games), 124 fmt.Sprintf("%0.2f", p.WinrateRecent()), 125 qmin.Round(time.Millisecond).String(), 126 qmean.Round(time.Millisecond).String(), 127 qmax.Round(time.Millisecond).String(), 128 qp50.Round(time.Millisecond).String(), 129 qp75.Round(time.Millisecond).String(), 130 qp95.Round(time.Millisecond).String(), 131 }) 132 } 133 csvw.Flush() 134 return nil 135 }