#include <stdio.h>

#ifdef WIN32
# include <windows.h>
#else
# include <sys/time.h>
# include <sys/times.h>
# include <unistd.h>
int GetTickCount()
{
  struct timeval t;
  gettimeofday(&t, NULL);
  return t.tv_sec * 1000 + t.tv_usec / 1000;
}
#endif

#define WHITE 8
#define BLACK 16
#define COLOR (WHITE|BLACK)

double genrand64_real2(void);
int MTinit(int t);

unsigned int MyRandom(unsigned int n)
{
  unsigned int res = n * genrand64_real2();
  return res;
}

typedef void Func(int stm, int from, int to, void* closure);
typedef int Board[128];

int value[8] = { 0, 100, 100, 10000, 325, 350, 500, 950 };
int firstDir[] = { 0, 0, 27, 4, 18, 8, 13, 4 };
int steps[] = { -16, -15, -17, 0, 1, -1, 16, -16, 15, -15, 17, -17, 0, 1, -1, 16, -16, 0, 18, 31, 33, 14, -18, -31, -33, -14, 0, 16, 15, 17, 0 };
Board PST =
{
  0, 2, 4, 6, 6, 4, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  2, 8,10,12,12,10, 8, 2, 0, 0, 0, 0, 0, 0, 0, 0,
  6,12,16,18,18,16,12, 6, 0, 0, 0, 0, 0, 0, 0, 0,
  8,14,18,20,20,18,14, 8, 0, 0, 0, 0, 0, 0, 0, 0,
  8,14,18,20,20,18,14, 8, 0, 0, 0, 0, 0, 0, 0, 0,
  6,12,16,18,18,16,12, 6, 0, 0, 0, 0, 0, 0, 0, 0,
  2, 8,10,12,12,10, 8, 2, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 2, 4, 6, 6, 4, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0
};


// "abcdefghijklmnopqrstuvwxyz"
char pieces[] = ".5........3..4.176........";
int pVal[] = { 0, 1, 1, 10, 3, 3, 5, 9 };
Board board, checkRay;
int Zobrist[8 * 128], posKey, cnt50;
int bestScore, bestFrom, bestTo, lastMover, lastChecker, randomize, post, king, seq, minors, alwaysQueen, biased, silent;

unsigned int nMoves[2], moveList[2][4096], keyHash[2][1024];

#define C1 40
#define C2 40
#define P1 250
#define P2 250
#define N1 75
#define N2 75
#define M1 60
#define M2 60
#define K1 50
#define K2 50
#define Q1 30
#define Q2 30

int strategy[2][8][8] =
{
  { {  0,  0,  0,  0,  0,  0,  0, 35 },
    {  1,400,400, P1, P1, P1, P1, 400 },
    {  1,400,400, P1, P1, P1, P1, 400 },
    {  1, K1, K1, K1, K1, K1, K1, K1 },
    {  1, N1, N1, N1,150, N1, N1, N1 },
    {  1, M1, M1, M1, M1,100, M1, 100 },
    {  1, C1, C1, C1, C1, C1, 60, 100 },
    {  1,  6,  6, Q1, 25, 80, 80, 100 },
  },
  { {  0,  0,  0,  0,  0,  0,  0, 35 },
    {  1,400,400, P2, P2, P2, P2, 400 },
    {  1,400,400, P2, P2, P2, P2, 400 },
    {  1, K2, K2, K2, K2, K2, K2, K2 },
    {  1, N2, N2, N2,150, N2, N2, N2 },
    {  1, M2, M2, M2, M2,100, M2, 100 },
    {  1, C2, C2, C2, C2, C2, 60, 100 },
    {  1,  6,  6, Q2, 25, 80, 80, 100 },
  },
};

int MoveGen (int stm, int lastTo)
{
  int move, from, to, piece, victim, type, dir, step, stop, vert, side = stm >> 4, n = 0;
  king = -1;
  seq++;

  for (from = 0; from < 128; from = from + 9 & ~8)
  {
    piece = board[from];

    if (piece) minors += 2 - ((piece & 6) == 4);

    if (piece & stm)
    {
      type = piece & 7;
      dir = firstDir[type];

      while ((step = steps[dir++]))
      {
        to = from;

        do
        {
          to += step;

          if (to & 0x88) break;

          stop = victim = board[to];

          if ((victim ^ piece) < WHITE) break; // captures friend

          if (type < 3)  // Pawn
          {
            int vert = !(to - from & 7);

            if (vert != !victim) break; // wrong mode

            move = 256 * from + to;

            if (vert && (type == 1 ? to > 79 : to < 48)) stop--; // precompensate leaper on 3rd rank

            if (type == 1 ? to < 16 : to > 111)  // promotion
            {
              move += (6 - side) << 16; // to Queen
              move += strategy[side][0][7] << 20;

              if (!alwaysQueen, 0)
                moveList[side][n++] = move - (1 << 16), // to Rook
                                      moveList[side][n++] = move - (2 << 16), // to Bishop
                                          moveList[side][n++] = move - (3 << 16); // to Knight
            }
          }
          else move = 256 * from + to;

          move += strategy[side][type][victim & 7] << 20;

          if ((victim & 7) == 3)  // king capture
          {
            king = to;

            if (lastTo != to)
              do
              {
                checkRay[to -= step] = seq; // mark check ray and attacker
              }
              while (to != from);

            return 1;
          }

          moveList[side][n++] = move;
          stop += type < 5;
        }
        while (!stop);
      }
    }
  }

  nMoves[side] = n;
  return 0;
}

int draw50, rep3, stalemate, insuf, checkmate[3];

void GameEnd (int result, char* msg)
{
  if (result) checkmate[result + 1]++;
  else if (*msg == '3') rep3++;
  else if (*msg == '5') draw50++;
  else if (*msg == 'i') insuf++;
  else if (*msg == 's') stalemate++;

  if (silent) return;

  printf("%s {%s}\n", result ? (result == 1 ? "1-0" : "0-1") : "1/2-1/2", msg);
  fflush(stdout);
}

int Pick (int side)
{
  int n = nMoves[side];

  if (!biased) return MyRandom(n);

  int i, w;

  for (i = w = 0; i < n; i++) w += moveList[side][i] >> 20;

  int x = MyRandom(w);

  for (i = w = 0; i < n; i++)
  {
    w += moveList[side][i] >> 20;

    if (w > x) return i;
  }

  return 0; // dead code
}

int DrawCheck(int stm, int from, int to, int piece, int victim)
{
  int side = stm >> 4;
  posKey += Zobrist[128 * (piece & 7) + to + stm - WHITE]
            - Zobrist[128 * (piece & 7) + from + stm - WHITE]
            - Zobrist[128 * (victim & 7) + to - stm + BLACK];
  int index = posKey & 1023;
  int key = keyHash[side][index];

  if ((posKey ^ key) & ~1023) keyHash[side][index] = posKey & ~1023; // no match; save position key
  else   // posKey was already hashed: repetition
  {
    if (keyHash[side][index]++ & 1)
    {
      GameEnd(0, "3-fold repetition");
      return 1;
    }
  }

  if (cnt50 >= 100)
  {
    GameEnd(0, "50-move rule");
    return 1;
  }

  cnt50++;

  if (victim || (piece & 7) < 3) cnt50 = 0;

  if (minors < 6)
  {
    GameEnd(0, "insufficient material");
    return 1;
  }

  return 0;
}

int Think (int stm)
{
  int from, to, piece, promo, pinSeq, pinned = -1, side = stm >> 4;

  while (nMoves[side])
  {
    int n = Pick(side);
    int move = moveList[side][n];
    to = move & 0xFF;
    from = move >> 8 & 0xFF, promo = move >> 16 & 0xF; // decode

    if (from == pinned && checkRay[to] != pinSeq)  // this moves a piece known to be pinned from the pin ray
    {
      moveList[side][n] = moveList[side][--nMoves[side]]; // delete the illegal move
      continue; // and try again
    }

    int piece = board[from];
    int victim = board[to];
    board[to] = piece + promo;
    board[from] = 0; // make move
    minors = 0; // set up to detect mating potential

    if (!MoveGen(stm ^ COLOR, to))
    {
      static char* name[] = { "", "", "k", "n", "b", "r", "q" };

      if (!silent) printf("move %c%d%c%d%s\n", (from & 7) + 'a', 8 - (from >> 4), (to & 7) + 'a', 8 - (to >> 4), name[promo + side]);

      if (DrawCheck(stm, from, to, piece, victim)) return 1;

      return 0;
    }

    board[from] = piece;
    board[to] = victim; // unmake move, as it was illegal

    if ((piece & 7) == 3)  // King stumbled into check
    {
      moveList[side][n] = moveList[side][--nMoves[side]]; // delete the illegal move and try again
    }
    else if (checkRay[from] == seq)    // we moved a pinned piece
    {
      pinned = from;
      pinSeq = seq; // remember the pinned piece
      moveList[side][n] = moveList[side][--nMoves[side]]; // delete the illegal move and try again
    }
    else     // we must have been in check all along
    {
      int i, j, n = nMoves[side];

      for (i = j = 0; i < n; i++) // examine all moves, to delete those that cannot solve this check
      {
        move = moveList[side][i];

        if ((move >> 8 & 0xFF) == king // King move
            || checkRay[move & 0xFF] == seq) // interposition or capture of checker
          moveList[side][j++] = move; // keep it
      }

      nMoves[side] = j; // try again with the cleaned-up move list
    }
  }

// none of the moves is legal!
  if (MoveGen(stm ^ COLOR, -1))  // we are in check
  {
    GameEnd(stm == WHITE ? -1 : 1, "checkmate");
  }
  else GameEnd(0, "stalemate");

  return 1; // flag game end
}

int Setup (char* fen)
{
  char c;
  int i;

  for (i = 0; i < 128; i++) board[i] = 0;

  i = 0;

  while ((c = *fen++))
  {
    if (c == 'p') board[i++] = BLACK + 2;
    else if (c >= '0' && c <= '9') i += c - '0';
    else if (c >= 'a' && c <= 'z') board[i++] = BLACK + pieces[c - 'a'] - '0';
    else if (c >= 'A' && c <= 'Z') board[i++] = WHITE + pieces[c - 'A'] - '0';
    else if (c == '/') i = (i | 15) + 1;
    else break;
  }

// for(i=0; i<128; i = i + 9 & ~8) printf(i&7 ? " %2d" : "\n# %2d", board[i]); printf("\n");
  c = (*fen == 'w' ? WHITE : BLACK);
  MoveGen(c, -1);
  return c;
}

int InitGame (char* fen)
{
  int i;

  if (!fen) fen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";

  for (i = 0; i < 1024; i++) keyHash[0][i] = keyHash[1][i] = 0;

  posKey = -1;
  cnt50 = 0;
  return Setup(fen);
}

char* fens[] =
{
  "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1",
  "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR b KQkq - 0 1",
  NULL,
  "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1",
};

void RunMatch()
{
  int i, f, t = GetTickCount();
  silent = 1; // suppress all CECP output

  for (i = f = 0; i < 1000000; i++)
  {
    int stm;

    if (!fens[f]) f = 0;

    stm = InitGame(fens[f++]);
    MoveGen(stm, -1);

    while (!Think(stm))  // play a game
    {
      stm ^= COLOR;
    }

    if (i % 1000 == 999) printf("%7d: %8.2f sec, %5d %5d %5d %5d %5d %6d %8d\n", i + 1, (GetTickCount() - t) / 1000.,
                                  checkmate[2], checkmate[0], stalemate, rep3, draw50, insuf, seq), fflush(stdout);
  }

  exit(0);
}

int main ()
{
  int i, stm = WHITE, engineSide = 0;
  char line[256], command[20];
  MTinit(GetTickCount());

  for (i = 0; i < 50; i++) MyRandom(1);

  for (i = 128; i < 128 * 8; i++) Zobrist[i] = MyRandom(-1);

  while (1)
  {
    int i, c;

    if (stm == engineSide)
    {
      if (Think(stm)) engineSide = 0;

      stm ^= COLOR;
    }

    fflush(stdout);
    i = 0;

    while ((line[i++] = c = getchar()) != '\n') if (c == EOF) printf("# EOF\n"), exit(1);

    line[i] = '\0';

    if (*line == '\n') continue;

    sscanf(line, "%s", command);
    printf("# command: %s\n", command);

    if (!strcmp(command, "usermove"))
    {
      int from, to;
      char c, d, promo, ep;
      sscanf(line, "usermove %c%d%c%d%c", &c, &from, &d, &to, &promo);
      from = (8 - from) * 16 + c - 'a';
      to = (8 - to) * 16 + d - 'a';
      int piece = board[from], victim = board[to];

      if ((piece & 7) == 3 && to - from == 2) board[from + 1] = board[to + 1], board[to + 1] = 0; // K-side castling

      if ((piece & 7) == 3 && from - to == 2) board[from - 1] = board[to - 2], board[to - 2] = 0; // Q-side castling

      ep = ((piece & 7) < 3 && !victim); // recognize e.p. capture
      board[to] = piece;

      if (ep) board[from & 0x70 | to & 7] = 0, victim = piece ^ COLOR;

      board[from] = 0;

      if (promo >= 'a')
      {
        board[to] = board[to] | 7; // promote to Q

        if (promo == 'n') board[to] -= 3; // correct for under-promotion

        if (promo == 'b') board[to] -= 2;

        if (promo == 'r') board[to] -= 1;
      }

      stm ^= COLOR;
      MoveGen(stm, to);

      if (DrawCheck(stm ^ COLOR, from, to, piece, victim)) engineSide = 0;
    }
    else if (!strcmp(command, "match")) RunMatch(); // not used as XBoard engine!
    else if (!strcmp(command, "protover")) printf("feature option=\"always queen -check 0\n"),
           printf("feature option=\"biased to capture -check 0\n"),
           printf("feature myname=\"Ram 2.0\" setboard=1 usermove=1 analyze=0 colors=0 sigint=0 sigterm=0 done=1\n");
    else if (!strcmp(command, "option")) sscanf(line + 7, "always queen=%d\n", &alwaysQueen),
           sscanf(line + 7, "biased to capture=%d\n", &biased);
    else if (!strcmp(command, "new")) stm = InitGame(NULL), randomize = 0, engineSide = BLACK;
    else if (!strcmp(command, "go")) engineSide = stm;
    else if (!strcmp(command, "result")) engineSide = 0;
    else if (!strcmp(command, "force")) engineSide = 0;
    else if (!strcmp(command, "setboard")) stm = Setup(line + 9);
    else if (!strcmp(command, "random")) randomize = !randomize;
    else if (!strcmp(command, "post")) post = 1;
    else if (!strcmp(command, "nopost")) post = 0;
    else if (!strcmp(command, "quit")) break;
  }

  return 0;
}
