Allow CSV format, multi runs, and fully customizable errors.

This commit is contained in:
Kistaro Windrider 2023-11-18 18:53:13 -08:00
parent 16f718dcdc
commit 0b68cb80bf
Signed by: kistaro
SSH Key Fingerprint: SHA256:TBE2ynfmJqsAf0CP6gsflA0q5X5wD5fVKWPsZ7eVUg8

View File

@ -1,11 +1,25 @@
package main package main
import ( import (
"encoding/csv"
"flag"
"fmt" "fmt"
"git.chromaticdragon.app/kistaro/auctionsim/auctionsim" "git.chromaticdragon.app/kistaro/auctionsim/auctionsim"
"log" "log"
"math"
"math/rand"
"os" "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 { func deltaStr(delta float64) string {
@ -18,25 +32,33 @@ func deltaStr(delta float64) string {
return "broken even" return "broken even"
} }
func main() { func normalizeFormat() error {
num := int64(1000) *format = strings.ToLower(*format)
if len(os.Args) > 1 { if *format == "txt" {
n, err := strconv.ParseInt(os.Args[1], 0, 64) return nil
if err != nil {
log.Fatalf("can't parse %q as a number of bidders: %v", os.Args[1], err)
} }
num = n if *format == "csv" {
return nil
} }
if num <= 1 { return fmt.Errorf("unrecognized format: %q", *format)
log.Fatalf("can't run an auction with %d bidders", num)
} }
summary := auctionsim.Summarize(auctionsim.RunAuctionVerbosely( type emitter interface {
&auctionsim.CappedBidderGenerator{ Emit(*auctionsim.ResultSummary)
G: auctionsim.NormalestBidderGenerator(), Flush()
Lim: num, }
},
)) 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 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("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.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) 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),
},
)))
}
}