// C++ version of auction simulation in F#, available at: http://gist.github.com/113912
#include "stdafx.h"
#define _CRT_RAND_S
typedef void *PVOID;
#include <stdlib.h>
#include <stdio.h>
#include <tchar.h>
#include <time.h>
#include <agents.h>
using namespace Concurrency;
enum MessageId { None, Inquire, Offer, Status, BestOffer, BeatenOffer, AuctionConcluded, AuctionFailed, AuctionOver };
struct AuctionReply {
AuctionReply() : msg_id(None) { }
AuctionReply(MessageId msg_id) : msg_id(msg_id) { }
AuctionReply(MessageId msg_id, int price) : msg_id(msg_id), best_offer(price) { }
AuctionReply(MessageId msg_id, ITarget<AuctionReply> *seller, ITarget<AuctionReply> *buyer) : msg_id(msg_id) { parties.buyer = buyer; parties.seller = seller;}
MessageId msg_id;
union {
int best_offer; // Inquire, BeatenOffer
struct { ITarget<AuctionReply> *seller; ITarget<AuctionReply> *buyer; } parties; // AuctionConcluded
};
};
struct AuctionMessage {
AuctionMessage() : msg_id(None) { }
AuctionMessage(MessageId msg_id, ITarget<AuctionReply> *reply_to) : msg_id(msg_id), reply_to(reply_to) { }
AuctionMessage(MessageId msg_id, int price, ITarget<AuctionReply> *reply_to) : msg_id(msg_id), price(price), reply_to(reply_to) { }
MessageId msg_id;
ITarget<AuctionReply> * reply_to; // Offer, Inquire
int price; // Offer
};
template<class Payload>
class actor : public agent
{
public:
operator ITarget<Payload> *() { return &inbox; }
operator ITarget<Payload> &() { return inbox; }
protected:
Payload receive() { return Concurrency::receive(inbox);}
bool tryreceive(Payload &msg, unsigned int timeout) { try { msg = Concurrency::receive(inbox, timeout); return true; } catch (operation_timed_out) { return false; } }
private:
unbounded_buffer<Payload> inbox;
};
class Auction : public actor<AuctionMessage>
{
public:
Auction(ITarget<AuctionReply> &seller, int min_bid, clock_t end_at) : seller(seller), min_bid(min_bid), end_at(end_at), max_bid(0), min_increment(10), max_bidder(NULL) {}
protected:
void run()
{
while (true)
{
clock_t remaining = end_at - clock();
auto msg = AuctionMessage();
if (tryreceive(msg, remaining))
{
switch (msg.msg_id)
{
case Inquire:
{
asend(msg.reply_to, AuctionReply(Status, max_bid));
break;
}
case Offer:
{
if (msg.price >= max_bid + min_increment)
{
if (max_bidder != NULL) asend(max_bidder, AuctionReply(BeatenOffer, msg.price));
max_bidder = msg.reply_to;
max_bid = msg.price;
asend(msg.reply_to, AuctionReply(BestOffer));
}
else
{
asend(msg.reply_to, AuctionReply(BeatenOffer, max_bid));
}
break;
}
}
}
else
{
if (max_bid >= min_bid)
{
auto reply = AuctionReply(AuctionConcluded, &seller, max_bidder);
asend(max_bidder, reply);
asend(seller, reply);
printf("auction succeeded, highest bid was %d\n", max_bid);
}
else
{
printf("auction failed, highest bid was %d\n", max_bid);
if (max_bidder != NULL) asend(max_bidder, AuctionReply(AuctionOver));
asend(seller, AuctionReply(AuctionFailed));
}
while (true)
{
if (tryreceive(msg, 3000))
{
switch (msg.msg_id)
{
case Offer:
asend(msg.reply_to, AuctionReply(AuctionOver));
break;
}
}
else
{
done(agent_done);
return;
}
}
}
}
}
private:
clock_t end_at;
ITarget<AuctionReply> & seller;
ITarget<AuctionReply> * max_bidder;
int min_bid, max_bid, min_increment;
};
unsigned int rand_in_range(unsigned int low, unsigned int high)
{
unsigned int number;
rand_s( &number );
return (unsigned int) ((double)number / ((double) UINT_MAX + 1 ) * high) + low;
}
class Client : public actor<AuctionReply>
{
public:
Client(ITarget<AuctionMessage> &auction, int id, int increment, int top) : auction(auction), id(id), increment(increment), top(top) {}
protected:
void run()
{
Log("starting");
asend(auction, AuctionMessage(Inquire, *this));
auto initial_status = receive();
bool is_done = false;
int max = initial_status.best_offer;
int current;
while (!is_done)
{
if (max > top) { Log("Too high for me!"); done(agent_done); return; }
current = max + increment;
Concurrency::wait(rand_in_range(1, 1000));
Log("making an offer at $", current);
asend(auction, AuctionMessage(Offer, current, *this));
bool has_best;
do
{
has_best = false;
auto reply = receive();
switch(reply.msg_id)
{
case BestOffer:
has_best = true; max = current;
break;
case BeatenOffer:
max = reply.best_offer;
break;
case AuctionConcluded:
case AuctionOver:
is_done = true;
break;
}
} while (has_best);
}
done(agent_done);
}
private:
void Log(char *msg) { printf("%d: %s\n", id, msg); }
void Log(char *msg, int value) { printf("%d: %s%d\n", id, msg, value); }
ITarget<AuctionMessage> & auction;
int id, increment, top;
};
class Seller : public actor<AuctionReply>
{
protected:
void run()
{
while (true)
switch (receive().msg_id)
{
case AuctionConcluded:
case AuctionFailed:
done(agent_done);
return;
}
}
};
int _tmain(int argc, _TCHAR* argv[])
{
const int CLIENT_COUNT = 15;
agent * agents[CLIENT_COUNT+2];
Seller seller;
seller.start();
Auction auction(seller, 100, clock()+6000);
auction.start();
agents[0] = &seller;
agents[1] = &auction;
for (int i = 0; i < CLIENT_COUNT; i++)
{
agents[i+2] = new Client(auction, i, rand_in_range(5, 25), rand_in_range(80, 450));
agents[i+2]->start();
}
agent::wait_for_all(CLIENT_COUNT+2, agents);
return 0;
}