/*
 * Headless self-play harness for the built-in AIs.
 * Build: make aitest
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#include "ai.h"
#include "kropcore.h"
#include "okp.h"

enum { AI_VIKTOR = 0, AI_TIGRAN = 1, AI_DUMBO = 2, AI_BENDER = 3 };

static double now_sec(void) {
  struct timespec ts;
  clock_gettime(CLOCK_MONOTONIC, &ts);
  return((double)ts.tv_sec + (double)ts.tv_nsec / 1e9);
}

static void call_ai(int which, const char *okp, int *x, int *y) {
  int *dbg = NULL;
  switch (which) {
    case AI_VIKTOR: ai_viktor(okp, x, y); break;
    case AI_TIGRAN: ai_tigran(okp, x, y, &dbg); break;
    case AI_DUMBO:  ai_dumbo(okp, x, y); break;
    case AI_BENDER: ai_bender(okp, x, y); break;
    default: *x = -1; *y = -1; break;
  }
}

static const char *ainame(int which) {
  switch (which) {
    case AI_VIKTOR: return("Viktor");
    case AI_TIGRAN: return("Tigran");
    case AI_DUMBO:  return("Dumbo");
    case AI_BENDER: return("Bender");
    default: return("?");
  }
}

static int play_game(int w, int h, int p1, int p2, int maxmoves, int verbose) {
  struct kropki_game *g;
  int moves = 0;
  double t0, t1, think[2] = {0, 0};
  int thinkn[2] = {0, 0};
  int sides[2];

  sides[0] = p1;
  sides[1] = p2;

  ai_viktor_reset();
  ai_tigran_reset();

  g = kropki_newgame((unsigned char)w, (unsigned char)h, 1);
  if (g == NULL) return(-1);

  while ((g->playablefields > 0) && (moves < maxmoves)) {
    char *okp;
    int x = -1, y = -1;
    int side = g->nextplayer - 1;
    int ai = sides[side];

    okp = kropki_struct2okp(g, NULL, NULL);
    if (okp == NULL) break;
    t0 = now_sec();
    call_ai(ai, okp, &x, &y);
    t1 = now_sec();
    free(okp);

    think[side] += (t1 - t0);
    thinkn[side]++;

    if ((x < 0) || (y < 0) || kropki_setpos(g, x, y, g->nextplayer, 0) != 0) {
      printf("illegal/failed move by %s at move %d: %d,%d\n", ainame(ai), moves, x, y);
      break;
    }
    moves++;
    if (verbose && ((moves % 20) == 0)) {
      printf("  move %d score %d-%d (last %s %.2fs)\n",
             moves, g->score[0], g->score[1], ainame(ai), t1 - t0);
    }
  }

  printf("%s vs %s on %dx%d: final %d-%d after %d moves | avg think %.2fs / %.2fs\n",
         ainame(p1), ainame(p2), w, h, g->score[0], g->score[1], moves,
         thinkn[0] ? think[0] / thinkn[0] : 0.0,
         thinkn[1] ? think[1] / thinkn[1] : 0.0);

  {
    int diff = (int)g->score[0] - (int)g->score[1];
    kropki_freegame(g);
    if (diff > 0) return(1);      /* p1 wins */
    if (diff < 0) return(2);      /* p2 wins */
    return(0);                    /* draw */
  }
}

int main(int argc, char **argv) {
  int games = 2;
  int maxmoves = 80; /* partial games for speed; enough for tactics to matter */
  int w = 20, h = 14; /* A7 */
  int i;
  int v_wins = 0, t_wins = 0, draws = 0;

  srand((unsigned)time(NULL));
  if (argc > 1) games = atoi(argv[1]);
  if (argc > 2) maxmoves = atoi(argv[2]);

  printf("=== timing single opening replies ===\n");
  {
    struct kropki_game *g = kropki_newgame(20, 14, 1);
    char *okp;
    int x, y;
    double t0, t1;
    kropki_setpos(g, 10, 7, 1, 0);
    okp = kropki_struct2okp(g, NULL, NULL);
    ai_viktor_reset();
    t0 = now_sec();
    ai_viktor(okp, &x, &y);
    t1 = now_sec();
    printf("Viktor reply to center: [%d,%d] in %.3fs\n", x, y, t1 - t0);
    free(okp);
    okp = kropki_struct2okp(g, NULL, NULL);
    ai_tigran_reset();
    t0 = now_sec();
    {
      int *dbg = NULL;
      ai_tigran(okp, &x, &y, &dbg);
    }
    t1 = now_sec();
    printf("Tigran reply to center: [%d,%d] in %.3fs\n", x, y, t1 - t0);
    free(okp);
    okp = kropki_struct2okp(g, NULL, NULL);
    t0 = now_sec();
    ai_bender(okp, &x, &y);
    t1 = now_sec();
    printf("Bender reply to center: [%d,%d] in %.3fs\n", x, y, t1 - t0);
    free(okp);
    kropki_freegame(g);
  }

  printf("\n=== Bender (P1) vs Tigran (P2), %d games, max %d moves ===\n", games, maxmoves);
  {
    int b_wins = 0, t_wins2 = 0, draws2 = 0;
    for (i = 0; i < games; i++) {
      int r = play_game(w, h, AI_BENDER, AI_TIGRAN, maxmoves, 1);
      if (r == 1) b_wins++;
      else if (r == 2) t_wins2++;
      else draws2++;
    }
    printf("Bender-vs-Tigran subtotal: Bender %d / Tigran %d / draw %d\n",
           b_wins, t_wins2, draws2);
  }

  printf("\n=== Viktor (P1) vs Tigran (P2), %d games, max %d moves ===\n", games, maxmoves);
  for (i = 0; i < games; i++) {
    int r = play_game(w, h, AI_VIKTOR, AI_TIGRAN, maxmoves, 1);
    if (r == 1) v_wins++;
    else if (r == 2) t_wins++;
    else draws++;
  }

  printf("\n=== Tigran (P1) vs Viktor (P2), %d games ===\n", games);
  for (i = 0; i < games; i++) {
    int r = play_game(w, h, AI_TIGRAN, AI_VIKTOR, maxmoves, 1);
    if (r == 2) v_wins++;
    else if (r == 1) t_wins++;
    else draws++;
  }

  printf("\nSUMMARY: Viktor wins=%d  Tigran wins=%d  draws/incomplete=%d\n", v_wins, t_wins, draws);
  return(0);
}
