import tango.core.Thread;

import tango.util.locks.Condition;
import tango.util.locks.Mutex;

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
  {
    this( int maxMeetings )
      {
        remaining_ = maxMeetings;
        lock_ = new Mutex;
        condition_ = new Condition( lock_ );
      }

    Color
    meet( Color my )
      {
        Color other = FADED;

        scope localLock = new ScopedLock( lock_ );
        if( firstCall_ )
          {
            if( remaining_ > 0 )
              {
                first_ = my;
                firstCall_ = false;
                condition_.wait();

                other = second_;

                --remaining_;
              }
          }
        else
          {
            firstCall_ = true;
            second_ = my;
            other = first_;

            condition_.notify();
          }

        return other;
      }

  private :
    int remaining_;

    Mutex lock_;
    Condition condition_;

    bool firstCall_ = true;
    Color first_, second_;
  }

class Creature : Thread
  {
    this(
        Color my,
        MeetingPlace meetingPlace )
      {
        super( &run );

        my_ = my;
        meetingPlace_ = meetingPlace;
      }

    void
    run()
      {
        while( true )
          {
            Color other = meetingPlace_.meet( my_ );
            if( FADED != other )
              {
                my_ = complement( other );
                ++creaturesMeet_;
              }
            else
              break;
          }
      }

    int
    total()
      {
        return creaturesMeet_;
      }

  private :
    Color my_;
    MeetingPlace meetingPlace_;

    int creaturesMeet_ = 0;

    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 meetings = 2 == args.length ? parse( args[ 1 ], 10 ) : 100;
    auto colors = [ BLUE, RED, YELLOW, BLUE ];
    auto meetingPlace = new MeetingPlace( meetings );

    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: