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  }