
#define CHECKMATE_SCORE INT32_MAX
#define QUEEN_VALUE 994
#define ROOK_VALUE 548
#define KNIGHT_VALUE 305
#define BISHOP_VALUE 350
#define PAWN_VALUE 100
#define PAWN_MOVE_SCORE 10
#define LEGAL_MOVE_VALUE 1
#define CASTLE_SCORE (2000 / (turn_count>>1))
#define CHECK_SCORE 1

#define STALEMATE_SCORE -4000
#define REPETITION_SCORE -6000

#define DEADEND_SCORE (-(2*QUEEN_VALUE))
#define DEADEND_DEPTH_LIMIT ROOK_VALUE

unsigned char depth1;
unsigned char depth2;
unsigned char blunders = 0;
unsigned long a;
unsigned char max_depth;
int global_deadend_score = DEADEND_SCORE;
int deadend_limit = DEADEND_DEPTH_LIMIT;

int check_stale(struct board *brd, struct coord king_pos, unsigned char depth);

long int sub_score(long int *score, unsigned add)
{
  long int retval = *score - add;
  if(retval > *score)
    retval = LONG_MIN;

  return retval;
}

unsigned ascend_value(unsigned char piece)
{
  if(piece == PC_QUEEN) return QUEEN_VALUE;
  else if(piece == PC_BISHOP) return BISHOP_VALUE;
  else if(piece == PC_ROOK) return ROOK_VALUE;
  else return KNIGHT_VALUE;
}

// return larger number if the opponent has more royal pieces
signed char rate_move(struct move *move, struct board *brd, unsigned char turn)
{
  struct board temp;
  static unsigned char depth = 0;
  struct move lmove;
  struct move opmax;
  struct coord *moves;
  struct coord pc_coords;
  struct coord king_pos;
  unsigned char move_count;

  struct piece_count cur_op_pc_count;
  struct piece_count mov_op_pc_count;
  struct piece_count cur_my_pc_count;

  unsigned char row, col;
  unsigned char ascend_required;
  unsigned char ascend;
  int32_t ascend_max_score;
  int32_t noascend_score;
  unsigned char stale;
  unsigned char piece;
  unsigned legal_move_count;
  unsigned char non_aggressiveness;
  signed char s, r, q;
  short int check_score = CHECK_SCORE;
  short int deadend_score = global_deadend_score;
  unsigned short pawn_move_score = PAWN_MOVE_SCORE;

#define OPP_MOVE (!(depth & 1))


  move->staledraw = move->ascend = move->score = 0;
  move->checkmate_found = UCHAR_MAX;

  if(depth >= max_depth)
    goto end;

//  fprintf(stderr, "%c%c-%c%c ", 'a' + move->src.col, 7-move->src.row+'1',
//                                'a' + move->dst.col, 7-move->dst.row+'1');

  temp = *brd;

  depth++;

  piece_count(brd, !turn, &cur_op_pc_count);

  piece_count(brd, turn, &cur_my_pc_count);

  // be very aggressive at beginning and endgame
  s = cur_op_pc_count.queen_count * 2 + cur_op_pc_count.rook_count + ((cur_op_pc_count.bishop_count + cur_op_pc_count.knight_count) >> 1);
  r = cur_my_pc_count.queen_count * 2 + cur_my_pc_count.rook_count + ((cur_my_pc_count.bishop_count + cur_my_pc_count.knight_count) >> 1);
  if(s <= 1)
  {
    non_aggressiveness = 1;
    if(r <= 2) pawn_move_score <<= 4;
    if(!s)
    {
      check_score = 50;
      deadend_score -= deadend_limit;
    }
  }
  else
  {
    non_aggressiveness = 1;
    if((q = cur_op_pc_count.queen_count - cur_my_pc_count.queen_count) > 0)
      non_aggressiveness += q;
    if((q = cur_op_pc_count.rook_count - cur_my_pc_count.rook_count) > 0)
      non_aggressiveness += q>>1;
    s = s - r;
  }

  a++;

  piece = piece_in_tile(brd, move->src.col, move->src.row);

  if(piece)
  {
    piece &= 0x7;
    if(piece == PC_KING)
    {
      if(abs(move->dst.col - move->src.col) == 2)
        move->score += CASTLE_SCORE;
    }

    else if(piece < PC_ROOK)
      move->score += pawn_move_score;
  }

  ascend_required = move_piece(&temp, move->src, move->dst);

  king_pos = find_king(&temp, !turn);

  piece_count(&temp, !turn, &mov_op_pc_count);

  move->score += ((signed)cur_op_pc_count.queen_count - mov_op_pc_count.queen_count) * QUEEN_VALUE;
  move->score += ((signed)cur_op_pc_count.rook_count - mov_op_pc_count.rook_count) * ROOK_VALUE;
  move->score += ((signed)cur_op_pc_count.knight_count - mov_op_pc_count.knight_count) * KNIGHT_VALUE;
  move->score += ((signed)cur_op_pc_count.bishop_count - mov_op_pc_count.bishop_count) * BISHOP_VALUE;
  move->score += ((signed)cur_op_pc_count.pawn_count - mov_op_pc_count.pawn_count) * PAWN_VALUE;

  stale = check_stale(&temp, king_pos, depth);

  if(turn_count > 3 && stale >= 4)
  {
    if(OPP_MOVE)
      move->score += REPETITION_SCORE;
    else
      move->score -= REPETITION_SCORE;
    move->staledraw = 1;
  }
  else if(stale == 2)
  {
    if(OPP_MOVE)
      move->score += STALEMATE_SCORE;
    else
      move->score -= STALEMATE_SCORE;
    if(depth <= 2)
      move->staledraw = 1;
  }
  else if(stale == 3)
  {
    if(!OPP_MOVE)
      move->score += CHECKMATE_SCORE - depth;
    else
      move->score += CHECKMATE_SCORE / depth;
    move->checkmate_found = depth;
  }
  else
  {
    if(stale == 1)
    {
        move->score += check_score;
    }

    if(ascend_required)
    {
      noascend_score = move->score;
      ascend_max_score = INT32_MIN;
      ascend = PC_ROOK;
    }

    do
    {
      if(ascend_required)
      {
        ascend_piece(&temp, move->dst, ascend);
        stale = check_stale(&temp, king_pos, -1);
        if(stale == 1)
          move->score = noascend_score + CHECK_SCORE;
        else
        {
          if(stale == 2)
            move->score = noascend_score + (OPP_MOVE ? STALEMATE_SCORE : -STALEMATE_SCORE);
          else if(stale == 3)
            move->score = noascend_score + ascend_value(ascend) + OPP_MOVE ? CHECKMATE_SCORE / depth : CHECKMATE_SCORE;
          else
            move->score = noascend_score + ascend_value(ascend);
          goto ascend;
        }
      }

      if(!OPP_MOVE ? (move->score > deadend_score + depth * deadend_limit)
                       : (move->score < -deadend_score - depth * deadend_limit))
      {
//        fprintf(stderr, "Calculating %s move, score: %i\n", OPP_MOVE ? "opponent's" : "my", move->score);
        opmax.score = INT32_MIN;
        for(legal_move_count=0,row=0;row<8;row++)
          for(col=0;col<8;col++)
          {
            if((piece = tile_occupied(&temp, row, col))
               && !((piece & PC_BLACK) ^ ((!turn) << 3)))
            {
              pc_coords.col = col;
              pc_coords.row = row;
              moves = legal_moves(&temp, pc_coords, &move_count, &king_pos);
              if(move_count)
              {
                legal_move_count += move_count;
                lmove.src = pc_coords;
                while(move_count--)
                {
                  lmove.dst = moves[move_count];
                  rate_move(&lmove, &temp, !turn);
                  if(opmax.score < lmove.score)
                    opmax = lmove;
                  if(lmove.staledraw)
                  {
                    if(depth < 2) move->staledraw = 1;
                  }
                  else
                  {
                    if(lmove.checkmate_found < move->checkmate_found)
                      move->checkmate_found = lmove.checkmate_found;
                    if(opmax.score >= CHECKMATE_SCORE)
                      break;
                  }
                }
                free(moves);
              }
            }
          }
        if(turn_count > 5)
          move->score -= LEGAL_MOVE_VALUE*(legal_move_count>>depth);
        move->score -= OPP_MOVE ? opmax.score << 1 : opmax.score / (depth * non_aggressiveness);
      }
/*      else
      {
        fprintf(stderr, "Stopping to %s move, score: %i\n", OPP_MOVE ? "opponent's" : "my", move->score);
      }*/
      if(ascend_required)
      {
        ascend:
        if(ascend_max_score <= move->score)
        {
          ascend_max_score = move->score;
          move->ascend = ascend;
        }
        ascend++;
      }
      else break;
    }
    while(ascend < PC_KING);
  }

  depth--;
  end:
  return s;
}

signed char rate_move_to_depth(struct move *move, unsigned char depth)
{
  signed char ret;

  max_depth = depth;
  ret = rate_move(move, &board, game_status.turn);

  if(move->checkmate_found != UCHAR_MAX)
    if(!graphics)
      printf("Found %s checkmate at depth %u\n", (move->checkmate_found & 1) ? "my" : "opponent's", move->checkmate_found);

  return ret;
}

struct piece_all_moves all_moves[8][8];

unsigned moves_above_score(int32_t score)
{
  unsigned char row, col;
  unsigned char count;
  unsigned retval = 0;

  for(row=0;row<8;row++)
    for(col=0;col<8;col++)
    {
      count = all_moves[row][col].move_count;
      while(count--)
        if(all_moves[row][col].moves[count].score >= score)
          retval++;
    }

  return retval;
}

#define BESTVAL_LIMIT 1000

struct move do_ai_move(struct coord king_pos)
{
  struct move *move;
  struct move max;
  unsigned char row, col;
  struct coord *moves;
  unsigned char move_count;
  struct coord pc_coords;
  unsigned char piece;
  char *ascends[] = { "rook", "knight", "bishop", "queen" };
  unsigned char blunder = 0;
  unsigned char d;
  unsigned char i;
  int32_t bestval = -CHECKMATE_SCORE + BESTVAL_LIMIT;
  struct piece_all_moves *piece_moves;
  int32_t bestval_limit2;
  unsigned total_moves;
  struct move max_nostale;
  signed char s;

  if(ai_opening(&max))
    return max;

  memset(all_moves, 0, sizeof(all_moves));
  memset(&max_nostale, 0, sizeof(struct move));
  max_nostale.score = INT32_MIN;

  d = depth1;

  for(a=0, total_moves=bestval_limit2=0, i=0; d<=depth2; d += depth2 - depth1, bestval = bestval_limit2, i++, a=0)
  {
    memset(&max, 0, sizeof(struct move));
    max.score = INT32_MIN;

    for(row=0;row<8;row++)
      for(col=0;col<8;col++)
      {
        if((piece = tile_occupied(&board, row, col))
           && !((piece & PC_BLACK) ^ (game_status.turn << 3)))
        {
          pc_coords.col = col;
          pc_coords.row = row;

          piece_moves = &all_moves[row][col];
          if(!i)
          {
            piece_moves->legal_moves = legal_moves(&board, pc_coords, &piece_moves->move_count, &king_pos);
            if(piece_moves->move_count)
            {
              piece_moves->moves = calloc(piece_moves->move_count, sizeof(struct move));
              if(!piece_moves->legal_moves || !piece_moves->moves)
                outofmemory();
            }
            else
              piece_moves->legal_moves = 0; // for free()
          }

          move_count = piece_moves->move_count;

          if(move_count)
          {
            moves = piece_moves->legal_moves;
            move = piece_moves->moves;
            total_moves += move_count;

            for(;move_count--;moves++,move++)
            {
              if(move->score < bestval)
                continue;

              move->src = pc_coords;
              move->dst = *moves;

              s = rate_move_to_depth(move, d);

              if(!i)
                bestval_limit2 += move->score;

              if((move->score > max.score) || (move->score == max.score && !(rand() & 7)))
              {
                max = *move;
                if(max.staledraw)
                {
                  if(max.score > (-CHECKMATE_SCORE / 4) || s < 2)
                  {
                    if(!graphics) puts("Trying to avoid draw...");
                  }
                  else goto select_stale;
                }
                else select_stale:max_nostale = max;
              }
              else if(blunders && !(rand() % blunders) && move->score < max.score)
              {
                max = *move;
                blunder = 1;
                break;
              }

              if(blunder || max.score >= CHECKMATE_SCORE) goto make_move;
            }
          }
        }
      }
    if(!graphics)
      printf("depth %u: Analyzed %lu possible situations.\n", d, a);

    if(turn_count <= 3) break;

    if(depth2 == depth1) break;

    if(!i)
    {
      bestval_limit2 /= total_moves;
      if(!graphics)
        printf("Total moves: %u, average score: %li\n", total_moves, (long)bestval_limit2);
      bestval_limit2 += (max.score - bestval_limit2) / 2;
      if(max.score < CHECKMATE_SCORE && (moves_above_score(bestval_limit2) <= 1 || max.score < 0))
      {
        bestval_limit2 = -CHECKMATE_SCORE + BESTVAL_LIMIT;
        if(!graphics)
          puts("Trying to find better moves...");
      }
      if(!graphics)
        printf("Recalculating moves with score >= %li using depth %u.\n", (long int)bestval_limit2, depth2);
    }

  }

  make_move:
  for(row=0;row<8;row++)
    for(col=0;col<8;col++)
    {
      free(all_moves[row][col].legal_moves);
      free(all_moves[row][col].moves);
    }

  if(max.staledraw && max_nostale.score != INT32_MIN)
    max = max_nostale;

  if(!graphics)
    printf("%u: Moving %c%c to %c%c (score: %li)\n", turn_count, 'a' + max.src.col, 7-max.src.row+'1',
                                'a' + max.dst.col, 7-max.dst.row+'1', (long int)max.score);

  if(move_piece(&board, max.src, max.dst))
  {
    if(!max.ascend) max.ascend = PC_QUEEN;
    ascend_piece(&board, max.dst, max.ascend);
    if(!graphics)
      printf("Ascended to %s\n", ascends[max.ascend - PC_ROOK]);
  }

  return max;
}
