package main import ( "encoding/csv" "flag" "fmt" "git.chromaticdragon.app/kistaro/auctionsim/auctionsim" "log" "math" "math/rand" "os" "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 { if delta > 0 { return fmt.Sprintf("gained ¤%f", delta) } if delta < 0 { return fmt.Sprintf("lost ¤%f", -delta) } return "broken even" } func normalizeFormat() error { *format = strings.ToLower(*format) if *format == "txt" { return nil } if *format == "csv" { return nil } return fmt.Errorf("unrecognized format: %q", *format) } 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) fmt.Println() if summary.LosersWithRegrets < 1 { fmt.Println("The item was not worth that to anybody.") } else { fmt.Printf("The item was worth that to %d bidders, who stopped bidding too soon.\n", summary.LosersWithRegrets) } fmt.Println() if summary.HighestValueRank > summary.Bidders { fmt.Println("Wow! It was infinitely beyond worthless to everybody.") } else { fmt.Printf("The bidder who would have gotten the most value was outbid by %d bidders.\n", summary.HighestValueRank-1) fmt.Printf("The item was worth at most ¤%f to them. Their maximum bid was ¤%f.\n", summary.HighestValue, summary.HighestValuatorBid) fmt.Printf("If they had paid the winning bid, they would have %s.\n", deltaStr(summary.MissedProfit)) 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), }, ))) } }