/*
 * 
 * MAML TUTORIAL (Model 4.1)
 * For full description refer to 
 * http://www.syslab.ceu.hu/maml/tutorial/
 * 
 * (c) 1998, CEU Systems Laboratory
 * 
 */

@model m4 {

  //Parameters of the model
  @var: int numOfOffers, maxOffer, changeInOffer, numOfParties, numOfCivilians,
            membersQuotient, fidelityFactor, maxWeight;

  @agent Party {
    @var: [] int offer;							
    // declares an int array variable
    @var: int members;

    // Setting the initial offer
    @sub: (void) init {
      @create [model->numOfOffers:i] int offer {			
      // create and initialize an int array
        offer[i] = model->maxOffer/2;
      }
      members = 0; 
    }

    // Changing the offer
    @sub: (void) newOffer { 
      int i;
      for (i=0; i<model->numOfOffers; i++) {
        offer[i] += [uniformIntRand getIntegerWithMin: -model->changeInOffer
                                              withMax:  model->changeInOffer]
                 + members/model->membersQuotient;
        if (offer[i] > model->maxOffer) offer[i]=model->maxOffer;
        if (offer[i] < 0 ) offer[i] = 0;
      }
    }
  }

  // The array of parties
  @var: [] Party parties;

  @agent Civilian {
    @var: int favouriteParty, fidelity;
    @var: [] int weights;


    @sub: (void) init {
    
      // Setting the preference weights randomly
      @create [model->numOfOffers:i] int weights {
        weights[i] = [uniformIntRand getIntegerWithMin: 0
                               withMax:  model->maxWeight];
      }
      fidelity = 0;
      favouriteParty = [uniformIntRand getIntegerWithMin: 0
                             withMax: model->numOfParties-1];
      model->parties[favouriteParty]->members++;
    }    

    // Calculating the weighted sum of the offers
    @sub: (int) weightedSumOf: ([] int) offers {			
    // a subprogram returning int
      int i;
      int sum = 0;
      for (i=0; i<model->numOfOffers; i++) sum += weights[i]*offers[i];
      return sum;
    }

    // Deciding which party to support
    @sub: (void) rethink {
      int insistence =
            [self weightedSumOf: model->parties[favouriteParty]->offer] +
            model->fidelityFactor*fidelity;
      int bestParty, i;
      int bestOffer = insistence;
      
      
      for (i=0; i<model->numOfParties; i++)
        if (i!=favouriteParty) {
          int iOffer = [self weightedSumOf: model->parties[i]->offer];
          if (iOffer>bestOffer) {
            bestOffer = iOffer;
            bestParty = i;
          }
        }
      if (bestOffer > insistence) {
        model->parties[favouriteParty]->members--;
        favouriteParty = bestParty;
        model->parties[favouriteParty]->members++;
        fidelity = 0;
      } else fidelity++;
    }
  }

  // Array of people
  @var: [] Civilian civilians;

  @planDef oneTurn seq {
    @forEach groupOfParty newOffer;
    @forEach groupOfCivilian rethink;
  }

  @schedule cyclic(1) {
    0: @plan oneTurn;
  }

@init:

  @create [numOfParties:i] Party parties { [parties[i] init]; }
  @create [numOfCivilians:i] Civilian civilians { [civilians[i] init]; }
  
}

@observe m4 {
@uses <analysis.h>;

  @extendAgent Civilian {
    @sub: (int) getParty { return favouriteParty; }
  }

  @probe: var "maxOffer", var "changeInOffer",
          var "numOfParties", var "numOfCivilians",
          var "membersQuotient", var "fidelityFactor",
          var "maxWeight", var "numOfOffers";

@var: id histogram;

@schedule cyclic (1) {
  0: @planDef {
       @to histogram reset;
       @to histogram update;
       @to histogram output;
     }
}

@init:

  numOfOffers = 3;
  maxOffer = 2000; changeInOffer = 100;
  numOfParties = 5; numOfCivilians = 300;
  membersQuotient = 1000; fidelityFactor = 10;
  maxWeight = 10;

  @buildProbes;
  [model probe];
  [controlPanel setStateStopped];


  @create EZBin histogram {
      [histogram setTitle: "Number Of Party Members"];
      [histogram setAxisLabelsX: "Parties" Y: "Number of members"];
      [histogram setBinCount: numOfParties];
      [histogram setLowerBound: 0];
      [histogram setUpperBound: numOfParties];
      [histogram setCollection: groupOfCivilian];
      [histogram setProbedSelector: M(getParty)];
  }
}