root / examples / auction / become.py

1
#!/usr/bin/env python
2
3
# cf. Scala by Example, Chapter 3
4
5
# Note: requires dramatis > 0.1.1
6
7
import time
8
import random
9
import inspect
10
import sys
11
import os.path
12
13
sys.path[0:0] = [ os.path.join( os.path.dirname( inspect.getabsfile( inspect.currentframe() ) ), '..', '..', 'lib' ) ]
14
15
import dramatis
16
17
class Auction(object):
18
19
    def __new__( cls, *args ):
20
        return Auction.Open( *args )
21
22
    class Open( dramatis.Actor ):
23
24
        def __init__(self, seller, min_bid, closing):
25
            self._seller = seller
26
            self._min_bid = min_bid
27
            self._closing = closing
28
            
29
            self._bid_increment = 10
30
            self._max_bid = self._min_bid - self._bid_increment
31
            self._max_bidder = None
32
33
            self.actor.refuse( "winner" )
34
            
35
            dramatis.release( self.actor.name ).close()
36
37
        def close(self):
38
            self.actor.actor_yield( self._closing - time.time() )
39
40
            if self._max_bid > self._min_bid:
41
                dramatis.release( self._seller ).winner( self._max_bidder )
42
                dramatis.release( self._max_bidder ).winner( self._seller )
43
                self.actor.become( Auction.Over( self._max_bidder,
44
                                                  self._max_bid ) )
45
            else:
46
                dramatis.release( self._seller ).failed( self._max_bid )
47
                self.actor.become( Auction.Over( None, self._max_bid ) )
48
                
49
        def inquire(self):
50
            return [ self._max_bid, self._closing ]
51
52
        def offer(self, bid, bidder):
53
            if bid >= self._max_bid + self._bid_increment:
54
                if self._max_bid >= self._min_bid:
55
                    dramatis.release( self._max_bidder ).beaten_offer( bid )
56
                self._max_bid = bid
57
                self._max_bidder = bidder
58
                return [ "best_offer", None ]
59
            else:
60
                return [ "beaten_offer", self._max_bid ]
61
62
        @property
63
        def winner(self): pass # this one is subtle ...
64
65
    class Over( dramatis.Actor.Behavior ):
66
        @property
67
        def winner(self): return self._winner
68
        @property
69
        def max_bid(self): return self._max_bid
70
71
        def __init__(self, winner, max_bid):
72
            self._winner = winner
73
            self._max_bid = max_bid
74
75
        def dramatis_bound(self):
76
            self.actor.accept( "winner" )
77
78
        def inquire(self, *args):
79
            [ self._max_bid, 0 ]
80
81
        def offer(self, *args):
82
            return [ "auction_over", self._max_bid ]
83
84
class Seller ( dramatis.Actor ):
85
    def winner(self, winner): pass
86
    def failed(self, highest_bid): pass
87
88
class Client ( dramatis.Actor ):
89
    @property
90
    def name(self): return self._name
91
92
    def __init__(self, name, increment, top, auction):
93
        self._name = name
94
        self._increment = increment
95
        self._top = top
96
        self._auction = auction
97
        self._current = 0
98
        self.log( "started" )
99
        self._max = auction.inquire()[0]
100
        self.log( "status " + str(self._max) )
101
        self.bid()
102
103
    def bid(self):
104
        if self._max > self._top:
105
            self.log("too high for me")
106
        elif ( self._current < self._max ):
107
            self._current = self._max + self._increment
108
            time.sleep( ( 1 + random.randint( 0, 1000 ) )/1000.0 )
109
            answer, max_bid = self._auction.offer( self._current,
110
                                                    self.actor.name )
111
            if answer == "best_offer":
112
                self.log("best offer: " + str(self._current))
113
            elif answer == "beaten_offer": self.beaten_offer( max_bid )
114
            elif answer == "auction_over":
115
                self.log("auction over, oh well")
116
117
    def beaten_offer(self, max_bid):
118
        self.log("beaten offer: " + str(max_bid))
119
        self._max = max_bid
120
        dramatis.release( self.actor.name ).bid()
121
122
    def winner(self, seller):
123
        self.log("I won!")
124
125
    def log(self,string):
126
        print ( "client %s: %s" % ( self._name, string ) )
127
128
# somebody gives up
129
130
seller = Seller()
131
auction = Auction( seller, 100, time.time() + 4 )
132
Client( "1a", 20, 200, auction )
133
Client( "1a", 10, 300, auction )
134
135
print "Notice: client %s won the first auction with a bid of %s" % \
136
       ( auction.winner.name, auction.max_bid )
137
138
# cut off while people still have money
139
140
seller = Seller()
141
auction = Auction( seller, 100, time.time() + 1.5 )
142
Client( "1b", 20, 200, auction )
143
Client( "2b", 10, 300, auction )
144
145
print "Notice: client %s won the first auction with a bid of %s" % \
146
       ( auction.winner.name, auction.max_bid )
147
148
# too expensive
149
150
seller = Seller()
151
auction = Auction( seller, 400, time.time() + 1.5 )
152
Client( "1c", 20, 200, auction )
153
Client( "2c", 10, 300, auction )
154
155
if auction.winner != None:
156
    raise RuntimeError
157
158
print "Notice: the third auction failed; the maximum recieved bid was %s" % \
159
       auction.max_bid
160