package auctionsim import ( "math" "slices" ) /** * RunAuction simulates a single-bid second-price auction with no reserve price * between all bidders emitted by the provided generator, returning the winning * bidder and how much they paid (the maximum bid of the second-highest bidder). * * To simulate an auction between the first several bidders emitted by some * generator, use CappedBidderGenerator (in generators.go). */ func RunAuction(g BidderGenerator) (float64, Bidder) { highBidder := Bidder{} maxBid := math.Inf(-1) prevBid := math.Inf(-1) for b, ok := g.Generate(); ok; b, ok = g.Generate() { if c := b.BidCeiling(); c > maxBid { prevBid = maxBid maxBid = c highBidder = b } } return prevBid, highBidder } /** * RunAuctionVerbosely simulates a single-bid second-price auction with no * reserve price between all bidders emitted by the provided generator, * returning all bidders sorted by the order they dropped out and how much the * winning bidder paid. * * Use CappedBidderGenerator to limit the size of the auction and therefore * the number of bidders on the list. */ func RunAuctionVerbosely(g BidderGenerator) float64, []Bidder { var bidders []Bidder for b, ok := g.Generate(); ok; b, ok = g.Generate() { bidders = append(bidders, b) } slices.SortFunc(bidders, func(a, b Bidder) int { // If both bid ceilings are infinite in the same direction, we get NaN here... d := a.BidCeiling() - b.BidCeiling() if d < 0 { return -1 } if d > 0 { return 1 } // ...which is fine, since NaN will fail both comparisons and return 0 here, so these // (incomparable) infinities get reported as equal, which is good enough. return 0 }) if len(bidders) <= 1 { // With one or zero bidders, there's no auction. The bidder (if any) gets it for the // reserve price, but there is no reserve price, so the auctioneer pays the sole bidder // an infinite amount of money to go away. Economics! return math.Inf(-1), bidders } // Final price is the second highest bidder's limit. return bidders[len(bidders)-2].BidCeiling(), bidders }