root / examples / auction / become.rb

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}"