Bid validation under tiered increments, idempotent retries, a soft close that extends on late bids, a deadline heap that closes auctions by time alone, and events for listeners.
Build the core of the online-auction round. Time comes from an injected Clock (the tests use a FakeClock you move by hand), money is whole rupees, and an auction closes by time alone: whoever asks next, or the timer's close_due(), moves it forward. Clock, FakeClock, Bid, Auction, AuctionEvent and BidRejected are given.
TieredIncrement().min_increment(current) (the lesson's IncrementPolicy: any object with min_increment plugs in, and listeners are any object with on_event, so neither needs a base class here): the smallest raise at a price: 50 below 1,000; 200 below 10,000; 1,000 below 100,000; 5,000 from there up.AuctionHouse(clock, increments, snipe_window=120.0, max_extensions=10):
subscribe(listener): listeners have on_event(event). A listener that raises must not stop the others.create(seller, title, start_price, reserve, starts_at, duration) returns an Auction (ids A1, A2...; ends_at = starts_at + duration; a non-positive price or duration raises ValueError).min_next_bid(auction): the start price with no bids, otherwise the leader's amount plus the increment at that amount.place_bid(bid_id, auction_id, bidder, amount) returns the Bid. A repeated bid_id returns the same bid and does nothing else. Otherwise, after moving the auction forward by time, reject with BidRejected unless the auction is open, the bidder is not the seller, not already the leader, and the amount is at least min_next_bid. An accepted bid is appended to auction.bids; the previous leader gets an outbid event. Soft close: a bid arriving less than snipe_window seconds before ends_at moves ends_at to now + snipe_window (at most max_extensions times) and sends an extended event to "watchers".cancel(auction_id, by): only the seller, only before the first bid, only while scheduled or open (otherwise BidRejected); the state becomes cancelled.close_due(): closes every auction whose deadline has passed (use a min-heap of (ends_at, id); an entry made stale by an extension must do nothing) and returns the auctions it closed. With a leader at or above the reserve the state is sold and two events go out (won to the bidder, sold to the seller); otherwise unsold (an unsold event to the seller).scheduled auction becomes open once now >= starts_at, and an open one past ends_at closes, as above, even if the timer has not run yet.JavaScript and TypeScript: camelCase (bidId, auctionId, minNextBid, closeDue, onEvent, startPrice, startsAt, endsAt, snipeWindow, maxExtensions), clock.now(); auction.leader is a getter.
Write your code, then run 12 example tests and 4 hidden tests. Each run is limited to 8 seconds: an infinite loop gets stopped and the runtime restarts, your code stays.