From 0b68cb80bfe4bffc699352acd4a4794f6f7ae39d Mon Sep 17 00:00:00 2001 From: Kistaro Windrider Date: Sat, 18 Nov 2023 18:53:13 -0800 Subject: [PATCH] Allow CSV format, multi runs, and fully customizable errors. --- main/main.go | 104 ++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 87 insertions(+), 17 deletions(-) diff --git a/main/main.go b/main/main.go index 9c45c5b..fd791e5 100644 --- a/main/main.go +++ b/main/main.go @@ -1,11 +1,25 @@ package main import ( + "encoding/csv" + "flag" "fmt" "git.chromaticdragon.app/kistaro/auctionsim/auctionsim" "log" + "math" + "math/rand" "os" - "strconv" + "strings" +) + +var ( + bidders = flag.Int("b", 1000, "Number of bidders in each auction.") + runs = flag.Int("r", 1, "Number of auctions to run.") + + errorDev = flag.Float64("d", 1.0, "Standard deviation of bidder irrationality.") + errorMean = flag.Float64("m", 0.0, "Mean of bidder irrationality. Negative values represent a bias towards underbidding.") + + format = flag.String("f", "txt", "Output format: plain text (\"txt\") or CSV (\"csv\").") ) func deltaStr(delta float64) string { @@ -18,25 +32,33 @@ func deltaStr(delta float64) string { return "broken even" } -func main() { - num := int64(1000) - if len(os.Args) > 1 { - n, err := strconv.ParseInt(os.Args[1], 0, 64) - if err != nil { - log.Fatalf("can't parse %q as a number of bidders: %v", os.Args[1], err) - } - num = n +func normalizeFormat() error { + *format = strings.ToLower(*format) + if *format == "txt" { + return nil } - if num <= 1 { - log.Fatalf("can't run an auction with %d bidders", num) + if *format == "csv" { + return nil } + return fmt.Errorf("unrecognized format: %q", *format) +} - summary := auctionsim.Summarize(auctionsim.RunAuctionVerbosely( - &auctionsim.CappedBidderGenerator{ - G: auctionsim.NormalestBidderGenerator(), - Lim: num, - }, - )) +type emitter interface { + Emit(*auctionsim.ResultSummary) + Flush() +} + +type textEmitter struct { + notFirst bool +} + +func (t *textEmitter) Emit(summary *auctionsim.ResultSummary) { + if t.notFirst { + fmt.Println() + fmt.Println("-----------------") + fmt.Println() + t.notFirst = true + } fmt.Printf("The auction winner paid ¤%f. They have %s.\n", summary.Price, deltaStr(summary.WinnerProfit)) fmt.Printf("The item was worth ¤%f to them.\n", summary.WinnerValue) fmt.Printf("They would have paid up to ¤%f for it.\n", summary.WinnerMaxBid) @@ -58,3 +80,51 @@ func main() { fmt.Printf("They valued it ¤%f more than the winning bidder.\n", summary.ValueDelta) } } + +func (*textEmitter) Flush() {} + +type csvEmitter struct { + w *csv.Writer +} + +func (c *csvEmitter) Emit(summary *auctionsim.ResultSummary) { + c.w.Write(summary.CSVRecord()) +} + +func (c *csvEmitter) Flush() { + c.w.Flush() +} + +func main() { + flag.Parse() + + if err := normalizeFormat(); err != nil { + log.Fatal(err) + } + + e := emitter(&textEmitter{}) + if *format == "csv" { + w := csv.NewWriter(os.Stdout) + e = &csvEmitter{w} + w.Write(auctionsim.ResultSummaryCSVHeader()) + } + defer e.Flush() + + for i := 0; i < *runs; i++ { + g := &auctionsim.BiddersFromDistributions{ + Values: auctionsim.NormalestDistribution(), + Irrationalities: &auctionsim.NormalDistribution{ + Rand: rand.New(rand.NewSource(int64(rand.Uint64()))), + StdDev: *errorDev, + Mean: *errorMean, + }, + Bankrolls: auctionsim.ConstDistribution(math.Inf(1)), + } + e.Emit(auctionsim.Summarize(auctionsim.RunAuctionVerbosely( + &auctionsim.CappedBidderGenerator{ + G: g, + Lim: int64(*bidders), + }, + ))) + } +}