| 1 | #!/usr/bin/env ruby
|
| 2 |
|
| 3 | # cf. Scala by Example, Chapter 3
|
| 4 |
|
| 5 | # Note: requires dramatis > 0.1.1
|
| 6 |
|
| 7 | $:.push File.join( File.dirname(__FILE__), "..", "..", "lib" )
|
| 8 |
|
| 9 | require 'dramatis/actor'
|
| 10 | require 'dramatis/actor/behavior'
|
| 11 |
|
| 12 | module Auction
|
| 13 |
|
| 14 | def self.new *args
|
| 15 | Open.new( *args )
|
| 16 | end
|
| 17 |
|
| 18 | class Open
|
| 19 | include Dramatis::Actor
|
| 20 | def initialize seller, min_bid, closing
|
| 21 | @seller = seller
|
| 22 | @min_bid = min_bid
|
| 23 | @closing = closing
|
| 24 |
|
| 25 | @bid_increment = 10
|
| 26 | @max_bid = @min_bid - @bid_increment
|
| 27 | @max_bidder = nil
|
| 28 |
|
| 29 | actor.refuse :winner
|
| 30 |
|
| 31 | release( actor.name ).close
|
| 32 | end
|
| 33 |
|
| 34 | def close
|
| 35 | actor.yield @closing - Time::now
|
| 36 |
|
| 37 | if @max_bid > @min_bid
|
| 38 | release( @seller ).winner @max_bidder
|
| 39 | release( @max_bidder ).winner @seller
|
| 40 | actor.become Over.new( @max_bidder, @max_bid )
|
| 41 | else
|
| 42 | release( @seller ).failed @max_bid
|
| 43 | actor.become Over.new( nil, @max_bid )
|
| 44 | end
|
| 45 | end
|
| 46 |
|
| 47 | def inquire
|
| 48 | [ @max_bid, @closing ]
|
| 49 | end
|
| 50 |
|
| 51 | def offer bid, bidder
|
| 52 | if bid >= @max_bid + @bid_increment
|
| 53 | if @max_bid >= @min_bid
|
| 54 | release( @max_bidder ).beaten_offer bid
|
| 55 | end
|
| 56 | @max_bid = bid
|
| 57 | @max_bidder = bidder
|
| 58 | :best_offer
|
| 59 | else
|
| 60 | [ :beaten_offer, @max_bid ]
|
| 61 | end
|
| 62 | end
|
| 63 | end
|
| 64 |
|
| 65 | class Over
|
| 66 | include Dramatis::Actor::Behavior
|
| 67 | attr_reader :winner, :max_bid
|
| 68 | def initialize winner, max_bid
|
| 69 | @winner = winner
|
| 70 | @max_bid = max_bid
|
| 71 | end
|
| 72 | def dramatis_bound
|
| 73 | actor.accept :winner
|
| 74 | end
|
| 75 | def inquire *args
|
| 76 | [ @max_bid, 0 ]
|
| 77 | end
|
| 78 | def offer *args
|
| 79 | :auction_over
|
| 80 | end
|
| 81 | end
|
| 82 |
|
| 83 | end
|
| 84 |
|
| 85 | class Seller
|
| 86 | include Dramatis::Actor
|
| 87 | def winner winner
|
| 88 | end
|
| 89 | def failed highest_bid
|
| 90 | end
|
| 91 | end
|
| 92 |
|
| 93 | class Client
|
| 94 | include Dramatis::Actor
|
| 95 | attr_reader :name
|
| 96 | def initialize name, increment, top, auction
|
| 97 | @name = name
|
| 98 | @increment = increment
|
| 99 | @top = top
|
| 100 | @auction = auction
|
| 101 | @current = 0
|
| 102 | log "started"
|
| 103 | @max = auction.inquire[0]
|
| 104 | log "status #{@max}"
|
| 105 | bid
|
| 106 | end
|
| 107 | def bid
|
| 108 | if @max > @top
|
| 109 | log("too high for me")
|
| 110 | elsif ( @current < @max )
|
| 111 | @current = @max + @increment
|
| 112 | sleep( ( 1 + rand( 1000 ) )/1000.0 )
|
| 113 | answer, max_bid = @auction.offer @current, actor.name
|
| 114 | case answer
|
| 115 | when :best_offer; log("best offer: #{@current}")
|
| 116 | when :beaten_offer; beaten_offer max_bid
|
| 117 | when :auction_over; log("auction over, oh well")
|
| 118 | end
|
| 119 | end
|
| 120 | end
|
| 121 | def beaten_offer max_bid
|
| 122 | log("beaten offer: #{max_bid}")
|
| 123 | @max = max_bid
|
| 124 | release( actor.name ).bid
|
| 125 | end
|
| 126 | def winner seller
|
| 127 | log "I won!"
|
| 128 | end
|
| 129 | def log string
|
| 130 | puts "client #{@name}: #{string}"
|
| 131 | end
|
| 132 | end
|
| 133 |
|
| 134 | # somebody gives up
|
| 135 |
|
| 136 | seller = Seller.new
|
| 137 | auction = Auction.new seller, 100, Time::now + 4
|
| 138 | Client.new "1a", 20, 200, auction
|
| 139 | Client.new "2a", 10, 300, auction
|
| 140 |
|
| 141 | puts "Notice: client #{auction.winner.name} won the first auction with a bid of #{auction.max_bid}"
|
| 142 |
|
| 143 | # cut off while people still have money
|
| 144 |
|
| 145 | seller = Seller.new
|
| 146 | auction = Auction.new seller, 100, Time::now + 1.5
|
| 147 | Client.new "1b", 20, 200, auction
|
| 148 | Client.new "2b", 10, 300, auction
|
| 149 |
|
| 150 | puts "Notice: client #{auction.winner.name} won the second auction with a bid of #{auction.max_bid}"
|
| 151 |
|
| 152 | # too expensive
|
| 153 |
|
| 154 | seller = Seller.new
|
| 155 | auction = Auction.new seller, 400, Time::now + 1.5
|
| 156 | Client.new "1c", 20, 200, auction
|
| 157 | Client.new "2c", 10, 300, auction
|
| 158 |
|
| 159 | raise RuntimeError if auction.winner != nil
|
| 160 |
|
| 161 | puts "Notice: the third auction failed; the maximum recieved bid was #{auction.max_bid}"
|