/*  RoShamBo Tournament Program           Darse Billings,  Sept 1999  */

#include <assert.h>
#include <stdio.h>         /* stdio.h */
#include <stdlib.h>        /* stdlib.h */
#include <string.h>        /* string.h */
#include <math.h>          /* math.h */
#include <time.h>          /* time.h */

#include "player.h"
#include "roster.h"

#define tourneys  1          /* number of round-robin tournaments */

#define fw        5          /* field width for printed numbers */
#define verbose1  0          /* print result of each trial */
#define verbose2  0          /* print match histories */
#define verbose3  1          /* print result of each match */

/*  Full History Structure (global variables, accessible to the
                            current player during each match)

      - element 0 is the number of trials played so far
      - element i is the action taken on turn i (1 <= i <= trials ) */

int my_history[trials+1], opp_history[trials+1];

/*  Tournament Crosstable Structure  */

#define nameleng  32
typedef struct {
    const char *name;        /* descriptive name (max 18 chars) */
    int(*pname)();           /* function name for computer player */
    int result[players+1];   /* list of player's match results */
} Player_Table;


#define maxrandom 2147483648.0   /* 2^31, ratio range is 0 <= r < 1 */

int flip_biased_coin (double prob)
{
    /* flip an unfair coin (bit) with given probability of getting a 1 */

    if ( (random() / maxrandom) >= prob )
         return(0);
    else return(1);
}

int biased_roshambo (double prob_rock, double prob_paper)
{
    /* roshambo with given probabilities of rock, paper, or scissors */
    double throw;

    throw = random() / maxrandom;

    if ( throw < prob_rock )                   { return(rock); }
    else if ( throw < prob_rock + prob_paper ) { return(paper); }
    else /* throw >= prob_rock + prob_paper */ { return(scissors); }
}

/*  RoShamBo Player Algorithms moved externally */

void Init_Player_Table (Player_Table crosstable[players+1])
{
    int i, j;

    crosstable[0].name = "Player Name";

    for (i = 0; i <= players; i++) {
        if (i > 0) {
            crosstable[i].name = roster[i - 1].name;
            crosstable[i].pname = roster[i - 1].bot;
        }
        for (j = 0; j <= players; j++) {
            crosstable[i].result[j] = 0;
        }
    }
}

int Play_Match ( int(*player1)(), int(*player2)() )
{
    /* play a match between two RoShamBo players */

    int i, j, p1, p2, p1total, p2total, ties;
    int p1hist[trials+1], p2hist[trials+1];

    p1total = 0; p2total = 0; ties = 0;

    for (i = 0; i <= trials; i++) {
        p1hist[i] = 0; p2hist[i] = 0;
        my_history[i] = 0; opp_history[i] = 0;
    }

    for (i = 1; i <= trials; i++) {

        /* provide copies of history arrays for each player */
        memcpy(my_history, p1hist, sizeof(int)*(trials+1));
        memcpy(opp_history, p2hist, sizeof(int)*(trials+1));

        p1 = player1 ();              /* get player1 action */
        if ( (p1 < 0) || (p1 > 2) ) {
            printf("Error: return value out of range.\n");
            p1 = (p1 % 3 + 3) % 3;    /* note: -5 % 3 = -2, not 1 */
        }

        memcpy(opp_history, p1hist, sizeof(int)*(trials+1));
        memcpy(my_history, p2hist, sizeof(int)*(trials+1));

        p2 = player2 ();             /* get player2 action */
        if ( (p2 < 0) || (p2 > 2) ) {
            printf("Error: return value out of range.\n");
            p2 = (p2 % 3 + 3) % 3;
        }

        p1hist[0]++; p1hist[p1hist[0]] = p1;
        p2hist[0]++; p2hist[p2hist[0]] = p2;

        if (verbose1) { printf(" p1 = %d, p2 = %d", p1, p2); }
        if (p1 == p2) {
            ties++;
            if (verbose1) { printf(" tie!\n"); } }
        else if ( (p1-p2 == 1) || (p1-p2 == -2) ) {
            p1total++;
            if (verbose1) { printf(" p1 wins!\n"); } }
        else if ( (p2-p1 == 1) || (p2-p1 == -2) ) {
            p2total++;
            if (verbose1) { printf(" p2 wins!\n"); } }
        else printf("Error: should not be reached.\n");
    }
    if (verbose2) {
        printf(" Full history of p1 (%d trials):\n", p1hist[0]);
        for (j = 1; j <= trials; j++) {
            printf(" %d", p1hist[j]); }
        printf("\n");
        printf(" Full history of p2 (%d trials):\n", p1hist[0]);
        for (j = 1; j <= trials; j++) {
            printf(" %d", p2hist[j]); }
        printf("\n");
    }
    if (verbose3) {
        printf(" Match: %*d (%*d+ %*d- %*d=)\n", fw, p1total-p2total,
                            fw-1, p1total, fw-1, p2total, fw-1, ties);
    }

    return (p1total - p2total);
}

void Print_T_Results (Player_Table crosstable[players+1])
{
    int i, j;

    printf("\n Tournament results: \n\n");
    printf("    ");
    printf("%-*s ", nameleng, crosstable[0].name);
    for (j = 1; j <= players; j++) {
        printf(" %*d", fw, j);
    }
    printf("  total\n\n");
    for (i = 1; i <= players; i++) {
        printf(" %2d ", i);
        printf("%-*s ", nameleng, crosstable[i].name);
        for (j = 1; j <= players; j++) {
            printf(" %*d", fw, crosstable[i].result[j]);
        }
        printf(" %*d \n", fw+1, crosstable[i].result[0]);
    }
    printf("\n");
}

void Print_Sorted_Results (Player_Table crosstable[players+1])
{
    int i, j, max, swap;
    const char *nameswap;

    Player_Table sorted[players+1];

    for (i = 0; i <= players; i++) {
        sorted[i].name = crosstable[i].name;
        for (j = 0; j <= players; j++) {
            sorted[i].result[j] = crosstable[i].result[j];
        }
    }

    for (i = 1; i <= players; i++) {
        max = i;
        for (j = i; j <= players; j++) {
            if ( (sorted[j].result[0] > sorted[max].result[0]) ) {
                max = j;
            }
        }
        nameswap = sorted[i].name;
        sorted[i].name = sorted[max].name;
        sorted[max].name = nameswap;
        for (j = 0; j <= players; j++) {
            swap = sorted[i].result[j];
            sorted[i].result[j] = sorted[max].result[j];
            sorted[max].result[j] = swap;
        }
        for (j = 1; j <= players; j++) {
            swap = sorted[j].result[i];
            sorted[j].result[i] = sorted[j].result[max];
            sorted[j].result[max] = swap;
        }
    }
    Print_T_Results (sorted);
}

void Play_Tournament (Player_Table crosstable[players+1])
{
    int i, j, score;

    for (i = 1; i <= players; i++) {
        for (j = i+1; j <= players; j++) {
            if (verbose3) { printf(" %-*s vs %-*s ", nameleng,
                crosstable[i].name, nameleng, crosstable[j].name); }
            score = Play_Match (crosstable[i].pname, crosstable[j].pname);
            crosstable[i].result[j] += score;
            crosstable[j].result[i] -= score;
        }
    }

    for (i = 1; i <= players; i++) {
        crosstable[i].result[0] = 0;
        for (j = 1; j <= players; j++) {
            crosstable[i].result[0] += crosstable[i].result[j];
        }
    }
    if (verbose2) { Print_T_Results (crosstable); }
}

int main() {

    int i;
    Player_Table crosstable[players+1];

    /* fixed or variable seed to the random() function */
    /* srandom(0); */
    srandom(time(0));

    Init_Player_Table (crosstable);

    printf(" Playing %d tournaments with %d trials per match...\n\n",
             tourneys, trials);

    for (i = 1; i <= tourneys; i++) {
        Play_Tournament (crosstable);
    }
    Print_Sorted_Results (crosstable);
    return(0);
}
