summaryrefslogtreecommitdiff
path: root/docs/examples/actors/auction.scala
blob: c3124c67a9bc1e2fa81a40d72f83274bafa4add7 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
package examples.actors

import java.util.Date

import scala.actors._
import scala.actors.Actor._

/** A simple demonstrator program implementing an online auction service
 *  The example uses the actor abstraction defined in the API of
 *  package scala.actors.
 */

trait AuctionMessage
case class Offer(bid: Int, client: Actor) extends AuctionMessage // make a bid
case class Inquire(client: Actor) extends AuctionMessage         // inquire status

trait AuctionReply
case class Status(asked: Int, expiration: Date)           // asked sum, expiration date
  extends AuctionReply
case object BestOffer extends AuctionReply                // yours is the best offer
case class BeatenOffer(maxBid: Int) extends AuctionReply  // offer beaten by maxBid
case class AuctionConcluded(seller: Actor, client: Actor) // auction concluded
  extends AuctionReply
case object AuctionFailed extends AuctionReply            // failed with no bids
case object AuctionOver extends AuctionReply              // bidding is closed

class AuctionActor(seller: Actor, minBid: Int, closing: Date) extends Actor {
  val timeToShutdown = 3000 // msec
  val bidIncrement = 10

  def act() {
    var maxBid = minBid - bidIncrement
    var maxBidder: Actor = null

    loop {
      reactWithin (closing.getTime() - new Date().getTime()) {

        case Offer(bid, client) =>
          if (bid >= maxBid + bidIncrement) {
            if (maxBid >= minBid)
              maxBidder ! BeatenOffer(bid)
            maxBid = bid
            maxBidder = client
            client ! BestOffer
          }
          else
            client ! BeatenOffer(maxBid)

        case Inquire(client) =>
          client ! Status(maxBid, closing)

        case TIMEOUT =>
          if (maxBid >= minBid) {
            val reply = AuctionConcluded(seller, maxBidder)
            maxBidder ! reply
            seller ! reply
          } else {
            seller ! AuctionFailed
          }
          reactWithin(timeToShutdown) {
            case Offer(_, client) => client ! AuctionOver
            case TIMEOUT => exit()
          }

      }
    }
  }
}

object auction {

  val random = new scala.util.Random

  val minBid = 100
  val closing = new Date(new Date().getTime() + 4000)

  val seller = Actor.actor { }
  val auction = new AuctionActor(seller, minBid, closing)

  def client(i: Int, increment: Int, top: Int) = new Actor {
    val name = "Client " + i
    def log(msg: String) = Console.println(name + ": " + msg)
    var max: Int = _
    var current: Int = 0
    def act() {
      log("started")
      auction ! Inquire(this)
      receive {
        case Status(maxBid, _) =>
          log("status(" + maxBid + ")")
          max = maxBid
      }
      loop {
        if (max >= top) {
          log("too high for me")
        }
        else if (current < max) {
          current = max + increment
          Thread.sleep(1 + random.nextInt(1000))
          auction ! Offer(current, this)
        }

        reactWithin(3000) {
          case BestOffer =>
            log("bestOffer(" + current + ")")

          case BeatenOffer(maxBid) =>
            log("beatenOffer(" + maxBid + ")")
            max = maxBid

          case AuctionConcluded(seller, maxBidder) =>
            log("auctionConcluded"); exit()

          case AuctionOver =>
            log("auctionOver"); exit()

          case TIMEOUT =>
            exit()
        }
      }
    }
  }

  def main(args: Array[String]) {
    seller.start()
    auction.start()
    client(1, 20, 200).start()
    client(2, 10, 300).start()
  }

}