(*
INTERFACE T34;
  PROCEDURE InitGame(NumberOfPlayers,PlayerID : INTEGER);
  PROCEDURE InitScoreValues(CardSymbol: CHAR; ScoreValue1,ScoreValue2 : INTEGER);
  PROCEDURE InitSuitColor(CardSuit,CardColor : CHAR);
  PROCEDURE InitRewardPoints(RewardPoints1,RewardPoints2,RewardPoints3 : INTEGER);
  PROCEDURE DealToPlayer(CardSymbol,CardSuit : CHAR);
  PROCEDURE DealToTable(CardSymbol,CardSuit : CHAR; ScoreValue : INTEGER);
  PROCEDURE InformPlay(PlayerID : INTEGER; CardSymbol,CardSuit : CHAR; ScoreValue : INTEGER);
  PROCEDURE PlayCard(CardSymbol,CardSuit : CHAR; ScoreValue,TableScore,Reward : INTEGER);
END T34;

Exceptions : NONE

Other Requirements : NONE

Assumptions:
InitGame is called before any other routine for each new game
PlayCard is only called after DealToPlayer has been called at least once for each game
State Invariants may be false until InitGame is called for each new game
Everything passed down from the main control program is valid (This eliminates some domain checking in code)
*)

MODULE T34;

IMPORT MCPParam;
CONST

  DealerID = 0;

TYPE

  SymbolScoresRec = RECORD;
    Symbol : CHAR;
    Score1 : INTEGER;
    Score2 : INTEGER;
  END;

  SuitColorsRec = RECORD;
    Suit : CHAR;
    Color : CHAR;
  END;

  HandRec = RECORD;
    Symbol : CHAR;
    Suit : CHAR;
  END;

  LogRec = RECORD;
    PlayerID : INTEGER;
    Symbol : CHAR;
    Suit : CHAR;
    Score : INTEGER;
    Reward : INTEGER;
  END;

PROCEDURE InitGame*(NumberOfPlayers, PlayerID: INTEGER);
  BEGIN
    NumPlayers := NumberOfPlayers;
    MyID := PlayerID;
    TScore := 0;
    SizeSymbolScores := 0;
    SizeSuitColors := 0;
    SizeHand := 0;
    SizeLog := 0;
  END InitGame;

PROCEDURE InitScoreValues*(CardSymbol: CHAR; ScoreValue1, ScoreValue2: INTEGER);
VAR
  i: INTEGER;
BEGIN
  i := 0;
  WHILE ((i < SizeSymbolScores) & (CardSymbol # SymbolScores[i].Symbol)) DO
    i := i + 1;
  END;
  IF i < SizeSymbolScores THEN
    SymbolScores[i].Score1 := ScoreValue1;
    SymbolScores[i].Score2 := ScoreValue2;
  ELSE
    SizeSymbolScores := SizeSymbolScores + 1;
    SymbolScores[SizeSymbolScores-1].Symbol := CardSymbol;
    SymbolScores[SizeSymbolScores-1].Score1 := ScoreValue1;
    SymbolScores[SizeSymbolScores-1].Score2 := ScoreValue2;
  END;
END InitScoreValues;

PROCEDURE InitSuitColor*(CardSuit, CardColor : CHAR);
VAR
  i : INTEGER;
BEGIN
  i := 0;
  WHILE ((i <= SizeSuitColors-1) & (SuitColors[i].Suit # CardSuit)) DO
    i := i + 1;
  END;
  IF i <= SizeSuitColors - 1 THEN
    SuitColors[i].Color := CardColor;
  ELSE
    SizeSuitColors := SizeSuitColors + 1;
    SuitColors[SizeSuitColors-1].Suit := CardSuit;
    SuitColors[SizeSuitColors-1].Color := CardColor;
 END;
END InitSuitColor;

PROCEDURE InitRewardPoints*(RewardPoints1, RewardPoints2, RewardPoints3:INTEGER);
BEGIN
	Reward1 := RewardPoints1;
	Reward2 := RewardPoints2;
	Reward3 := RewardPoints3;
END InitRewardPoints;

PROCEDURE DealToPlayer*(CardSymbol,CardSuit: CHAR);
BEGIN
  Hand[SizeHand].Symbol := CardSymbol;
  Hand[SizeHand].Suit := CardSuit;
  SizeHand := SizeHand + 1;
END DealToPlayer;

PROCEDURE DealToTable*(CardSymbol,CardSuit: CHAR; ScoreValue: INTEGER);
  BEGIN
    TScore := TScore + ScoreValue;
    Log[SizeLog].Reward := 0;
    Log[SizeLog].Suit := CardSuit;
    Log[SizeLog].Score := ScoreValue;
    Log[SizeLog].Symbol := CardSymbol;
    Log[SizeLog].PlayerID := DealerID;
    SizeLog := SizeLog + 1;
  END DealToTable;

PROCEDURE InformPlay*(PlayerID: INTEGER; CardSymbol,CardSuit: CHAR; ScoreValue: INTEGER);
  PROCEDURE DivisibleByGameSpecialNum (x: INTEGER): BOOLEAN;
    BEGIN
      RETURN ((x MOD MCPParam.GameSpecialNum)=0);
    END DivisibleByGameSpecialNum;
  PROCEDURE ContainsGameSpecialNum (w: INTEGER): BOOLEAN;
    VAR i,Temp,x: INTEGER;
       result: BOOLEAN;
    BEGIN
      x := ABS(w);
      result := FALSE;
      Temp:=1;
      FOR i:= 0 TO 3 DO
        IF (((x)DIV(Temp))MOD(MCPParam.GameNumRepBase)=MCPParam.GameSpecialNum) THEN
	  result := TRUE;
	END;
	Temp:=Temp*MCPParam.GameNumRepBase;
      END;
      RETURN result;
    END ContainsGameSpecialNum;
  BEGIN
    IF (PlayerID # MyID) THEN
      TScore := TScore + ScoreValue;
      IF (ContainsGameSpecialNum(TScore)) THEN
        IF (DivisibleByGameSpecialNum(TScore)) THEN
          Log[SizeLog].Reward := Reward3;
        ELSE
          Log[SizeLog].Reward := Reward2;
        END;
      ELSE
        IF (DivisibleByGameSpecialNum(TScore)) THEN
          Log[SizeLog].Reward := Reward1;
        ELSE
          Log[SizeLog].Reward := 0;
        END;
      END;
      Log[SizeLog].Suit := CardSuit;
      Log[SizeLog].Score := ScoreValue;
      Log[SizeLog].Symbol := CardSymbol;
      Log[SizeLog].PlayerID := PlayerID;
      SizeLog := SizeLog + 1;
    END;
  END InformPlay;

PROCEDURE PlayCard*(VAR CardSymbol,CardSuit: CHAR; VAR ScoreValue,TableScore,Reward: INTEGER);
VAR
  ValidList: ARRAY (MCPParam.MaxNumSymbols * MCPParam.MaxNumSuits) OF HandRec;
  BestCard : HandRec;
  SizeValidList,x: INTEGER;
BEGIN

  ReturnValidList(ValidList, SizeValidList);
  TableScore:=TScore;  (*added statement*)
  Strategy(ValidList, SizeValidList, BestCard, ScoreValue, TableScore, Reward);

  TScore:= TableScore;
  CardSymbol := BestCard.Symbol;
  CardSuit := BestCard.Suit;

  (* Upgrading the log *)
  Log[SizeLog].PlayerID := MyID;
  Log[SizeLog].Symbol := CardSymbol;
  Log[SizeLog].Suit := CardSuit;
  Log[SizeLog].Score := ScoreValue;
  Log[SizeLog].Reward := Reward;
  SizeLog := SizeLog + 1;

  (*Removing card from hand*)
  x := 0;
  WHILE ((Hand[x].Symbol # CardSymbol) OR (Hand[x].Suit # CardSuit)) DO
    x := x + 1;
  END;
  Hand[x].Symbol := Hand[SizeHand-1].Symbol;
  Hand[x].Suit := Hand[SizeHand-1].Suit;
  SizeHand := SizeHand - 1;
END PlayCard;

PROCEDURE ReturnValidList(VAR ValidList : ARRAY OF HandRec; VAR SizeValidList : INTEGER);
  VAR x, y : INTEGER;
      LastColor, LastSuit : CHAR;
  BEGIN
	SizeValidList := 0;
	LastSuit  := Log[SizeLog - 1].Suit;

	FOR x := 0 TO SizeSuitColors - 1 DO
		IF (SuitColors[x].Suit = Log[SizeLog - 1].Suit) THEN
			LastColor := SuitColors[x].Color;
		END;
	END;

	FOR x := 0 TO SizeSuitColors - 1 DO
		FOR y := 0 TO SizeHand - 1 DO
			IF (SuitColors[x].Suit = Hand[y].Suit) & ~(LastColor = SuitColors[x].Color) THEN
				ValidList[SizeValidList].Symbol := Hand[y].Symbol;
				ValidList[SizeValidList].Suit := Hand[y].Suit;
				SizeValidList := SizeValidList + 1;
			END;
		END;
	END;

	IF SizeValidList = 0 THEN
		FOR y := 0 TO SizeHand - 1 DO
			IF ~(Hand[y].Suit = LastSuit) THEN
				ValidList[SizeValidList].Symbol := Hand[y].Symbol;
				ValidList[SizeValidList].Suit := Hand[y].Suit;
				SizeValidList := SizeValidList + 1;
			END;
		END;
	END;

	IF (SizeValidList = 0) THEN
		FOR y:= 0 TO SizeHand-1 DO
			ValidList[SizeValidList].Symbol := Hand[y].Symbol;
			ValidList[SizeValidList].Suit := Hand[y].Suit;
			SizeValidList := SizeValidList + 1;
		END;
	END;
END ReturnValidList;

PROCEDURE Strategy ( ValidList:ARRAY OF HandRec;
                     SizeValidList:INTEGER;
                     VAR BestCard: HandRec;
                     VAR ScoreValue:INTEGER;
                     VAR TableScore:INTEGER;
                     VAR Reward:INTEGER
                    );
VAR i, maxindex: INTEGER;
    MaxBenefit: REAL;
    EXPReward: REAL;
    StdDiv, w0, w1, w2, w3:REAL;

   PROCEDURE DivisibleBy (x, y: INTEGER): BOOLEAN;
     BEGIN
       RETURN (((x) MOD (y))=0);
     END DivisibleBy;


  PROCEDURE Contains (t, y, base: INTEGER): BOOLEAN;
  VAR
    i, power,x: INTEGER;
    result: BOOLEAN;
  BEGIN
    x := ABS(t);
    result := FALSE;
    power  :=1;
    FOR i:= 0 TO 3 DO
      IF (((x)DIV(power))MOD(base)=y) THEN
        result := TRUE;
      END;
      power:=power*base;
    END;
    RETURN result;
  END Contains;


  PROCEDURE GetReward (TScore:INTEGER): INTEGER;
    VAR
      num,base: INTEGER;
    BEGIN
      num := MCPParam.GameSpecialNum;
      base:= MCPParam.GameNumRepBase;
      IF ( DivisibleBy(TScore,num) & ( ~ Contains(TScore,num,base) )) THEN
        RETURN Reward1;
      ELSIF (( ~ DivisibleBy(TScore,num) ) & Contains (TScore, num,base)) THEN
        RETURN Reward2;
      ELSIF ( DivisibleBy(TScore,num) & Contains(TScore,num,base) )THEN
        RETURN Reward3;
      ELSIF (( ~ DivisibleBy(TScore,num) ) & ( ~ Contains (TScore,num,base) )) THEN
        RETURN 0;
      END;
  END GetReward;

   PROCEDURE GetScore(CardSymbol: CHAR; ScoreSelection: INTEGER): INTEGER;
     VAR i,result: INTEGER;
     BEGIN
       i:=0;
       WHILE (SymbolScores[i].Symbol#CardSymbol) DO
         i:=i+1;
       END;
       IF (ScoreSelection=1) THEN result:= SymbolScores[i].Score1;
       ELSIF (ScoreSelection=2) THEN result:= SymbolScores[i].Score2;
       END;
       RETURN result;
     END GetScore;

   PROCEDURE GetColor(CardSuit: CHAR): CHAR;
   (*Get the color of a suit*)
     VAR i: INTEGER;
     BEGIN
       i := 0;
       WHILE (SuitColors[i].Suit # CardSuit) DO i := i+1; END;
       RETURN SuitColors[i].Color;
     END GetColor;

   PROCEDURE CanReduceColor (CardSymbol: CHAR; CardSuit: CHAR): INTEGER;
   (*check if number of colors can be reduced by playing input card*)
     VAR result, i: INTEGER;
         CardColor: CHAR;
     BEGIN
       result := 1;
       CardColor := GetColor(CardSuit);
       FOR i:= 0 TO SizeHand-1 DO
         IF ( (GetColor(Hand[i].Suit)=CardColor)
	    &
	    ((Hand[i].Symbol#CardSymbol)OR(Hand[i].Suit#CardSuit)) )
	 THEN result := 0;
	 END;
       END;
       RETURN result;
     END CanReduceColor;

   PROCEDURE CanReduceSuit (CardSymbol: CHAR; CardSuit: CHAR): INTEGER;
   (*check if number of suits can be reduced by playing input card*)
     VAR result, i: INTEGER;
     BEGIN
       result := 1;
       FOR i:= 0 TO SizeHand-1 DO
         IF ((Hand[i].Suit=CardSuit) & (Hand[i].Symbol#CardSymbol)) THEN result := 0; END;
       END;
       RETURN result;
     END CanReduceSuit;

   PROCEDURE HaveOneScore(CardSymbol: CHAR): INTEGER;
   (*Check if a card have only one score*)
     VAR result: INTEGER;
     BEGIN
       IF (GetScore(CardSymbol,1)=GetScore(CardSymbol,2)) THEN result:=1;
       ELSE result:=0;
       END;
       RETURN result;
     END HaveOneScore;

   PROCEDURE NumCopy (ScoreValue: INTEGER): INTEGER;
   (*Return how many card in hands can provied input score*)
   VAR result, i: INTEGER;
   BEGIN
     result:=0;
     FOR i:=0 TO SizeHand-1 DO
       IF (GetScore(Hand[i].Symbol,1)=ScoreValue) OR (GetScore(Hand[i].Symbol,2)=ScoreValue) THEN
         result:=result+1;
       END;
     END;
     RETURN result;
   END NumCopy;

   PROCEDURE NumColors (Suit: CHAR): INTEGER;
   (*Return the number of colors in current game*)
   VAR result,i,j,temp : INTEGER;
   BEGIN
     result:=1;
     FOR i:= 1 TO SizeSuitColors-1 DO
        temp:=1;
        FOR j:= 0 TO i-1 DO
          IF SuitColors[j].Color=SuitColors[i].Color THEN temp:=0;END;
        END;
        result:=result+temp;
     END;
     RETURN result;
   END NumColors;

   PROCEDURE GetBenefit (CardSymbol: CHAR; CardSuit: CHAR; ScoreCode: INTEGER; CurrentTScore: INTEGER): REAL;
   (*Directly called by body of Strategy*)
     VAR WeightColor,WeightSuit,WeightScore, result: REAL;
     BEGIN
       WeightColor:= StdDiv*PW((SizeHand-1)/SizeHand,2)/NumColors(GetColor(CardSuit));
       WeightSuit:= StdDiv*PW((SizeHand-1)/SizeHand,2)/SizeSuitColors;
       WeightScore:=StdDiv/SizeHand;
       result :=  GetReward(CurrentTScore+GetScore(CardSymbol, ScoreCode))
                 +WeightSuit*CanReduceSuit(CardSymbol,CardSuit)
		 +WeightColor*CanReduceColor(CardSymbol,CardSuit)
		 +WeightScore*HaveOneScore(CardSymbol)
		 +WeightScore*NumCopy(GetScore(CardSymbol,ScoreCode));
       RETURN result;
     END GetBenefit;

   PROCEDURE PW(x:REAL;y:INTEGER):REAL;
   (*A power function*)
   VAR result: REAL;
       c     : INTEGER;
   BEGIN
     result:=1;
     FOR c:= 1 TO y DO
       result:=result*x;
     END;
     RETURN result;
   END PW;


BEGIN
  (* Initialization *)
  maxindex := 0;
  ScoreValue := -10000;
  Reward := -10000;
  MaxBenefit := -10000;
  (*
  Initialize some Statistics properity
  w0,w1,w2,w3: posbility of getting 0, Reward1, Reward2, Reward3
  EXPReward: Expected reward of a move
  StdDiv   : Present how is the Reward points distribute
  These values are used by function GetBenefit
  *)
  w1 := (1/MCPParam.GameSpecialNum);
  w2 := 1-PW((MCPParam.GameNumRepBase-1)/MCPParam.GameNumRepBase,3);
  w3 := w1*w2;
  w2 := w2-w3;
  w1 := w1-w3;
  w0 := 1-w1-w2-w3;
  EXPReward:=w1*Reward1+w2*Reward2+w3*Reward3;
  StdDiv   :=w1*ABS(Reward1-EXPReward)+w2*ABS(Reward2-EXPReward)+w3*ABS(Reward3-EXPReward);
  (*
    Choose which card in ValidList should be played.
    The card gains the maximum benefit will be chosen.
    Assign values to parameters BestCard,
    which are called by reference.
  *)
  FOR i := 0 TO (SizeValidList - 1) DO
    IF ( GetBenefit(ValidList[i].Symbol, ValidList[i].Suit, 1, TableScore) > MaxBenefit ) THEN
        MaxBenefit := GetBenefit(ValidList[i].Symbol, ValidList[i].Suit, 1, TableScore);
        maxindex := i;
        Reward := GetReward( GetScore(ValidList[i].Symbol,1)+TableScore );
        ScoreValue := GetScore (ValidList[i].Symbol, 1);
      END;
    IF ( GetBenefit(ValidList[i].Symbol, ValidList[i].Suit, 2, TableScore)> MaxBenefit ) THEN
        maxindex := i;
	MaxBenefit := GetBenefit(ValidList[i].Symbol, ValidList[i].Suit, 2, TableScore);
        Reward := GetReward( GetScore(ValidList[i].Symbol,2)+TableScore );
        ScoreValue := GetScore (ValidList[i].Symbol, 2);
      END;
  END;
  (*
     Return values to parameter list
  *)
  BestCard.Symbol := ValidList[maxindex].Symbol;
  BestCard.Suit   := ValidList[maxindex].Suit;
  TableScore := TableScore+ScoreValue;
END Strategy;


VAR
  NumPlayers,Reward1,Reward2,Reward3,MyID,TScore : INTEGER;
  SizeSymbolScores,SizeSuitColors,SizeHand,SizeLog : INTEGER;

  SymbolScores : ARRAY MCPParam.MaxNumSymbols OF SymbolScoresRec;
  SuitColors : ARRAY MCPParam.MaxNumSuits OF SuitColorsRec;
  Hand : ARRAY (MCPParam.MaxNumSymbols * MCPParam.MaxNumSuits) OF HandRec;
  Log : ARRAY (MCPParam.MaxNumSymbols * MCPParam.MaxNumSuits) OF LogRec;

BEGIN
END T34.

