import tango.core.Thread;
import tango.util.locks.Condition;
import tango.util.locks.Mutex;
import tango.util.locks.Semaphore;
import tango.text.convert.Integer;
import tango.io.Stdout;
typedef int Color;
const Color BLUE = 0;
const Color RED = 1;
const Color YELLOW = 2;
const Color FADED = 3;
class MeetingPlace
{
int remaining_ = 10;
Semaphore sem_;
Mutex lock_;
Condition meetCompleted_;
Color * swappedColor_ = null;
this()
{
sem_ = new Semaphore(2);
lock_ = new Mutex;
meetCompleted_ = new Condition( lock_ );
}
}
class Creature : Thread
{
this(
Color my,
MeetingPlace meetingPlace )
{
super( &run );
my_ = my;
meetingPlace_ = meetingPlace;
}
void
run()
{
while( true )
{
Color other = processMeeting;
if( FADED != other )
{
my_ = complement( other );
++creaturesMeet_;
}
else
{
my_ = FADED;
break;
}
}
}
int
total()
{
return creaturesMeet_;
}
private :
Color my_;
MeetingPlace meetingPlace_;
int creaturesMeet_ = 0;
Color
processMeeting()
{
Color other = my_;
with( meetingPlace_ )
{
sem_.acquire();
scope localLock = new ScopedLock( lock_ );
if( !swappedColor_ )
{
scope(exit) sem_.release();
if( remaining_ )
{
scope(exit) sem_.release();
swappedColor_ = &other;
// Wait for second creature to make pair.
meetCompleted_.wait();
// Clean stuff after the meeting.
swappedColor_ = null;
--remaining_;
}
else
other = FADED;
}
else
{
// Color exchange.
other = *swappedColor_;
*swappedColor_ = my_;
// The first creature may wake up.
meetCompleted_.notify();
}
}
return other;
}
Color
complement( Color other )
{
switch( my_ )
{
case BLUE:
return other == RED ? YELLOW : RED;
case RED:
return other == BLUE ? YELLOW : BLUE;
case YELLOW:
return other == BLUE ? RED : BLUE;
default:
break;
}
return my_;
}
}
void
main( char[][] args )
{
auto meetingPlace = new MeetingPlace;
if( 2 == args.length )
meetingPlace.remaining_ = parse( args[ 1 ], 10 );
auto colors = [ BLUE, RED, YELLOW, BLUE ];
Creature[] group;
foreach( color; colors )
{
Creature creature = new Creature( color, meetingPlace );
group ~= creature;
creature.start;
}
int total = 0;
foreach( c; group )
{
c.join;
total += c.total;
}
Stdout( total ).newline;
}
// vim:ts=2:sts=2:sw=2:expandtab:fenc=utf-8: