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[MAX_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 char capture;
  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;
  unsigned pawn_value = PAWN_VALUE;

#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);
  piece = piece_in_tile(brd, move->coords.src.row, move->coords.src.col);
  if(s <= 1)
  {
    non_aggressiveness = 1;
    if(s <= 2)
    {
      capture = piece_in_tile(brd, move->coords.src.row, move->coords.src.col);
      if(capture && (capture & 0x7) < PC_ROOK)
        pawn_value <<= 2;
    }
    if(r <= 2)
    {
      pawn_value <<= 2;
      pawn_move_score <<= 4;
      pawn_move_score *= (piece & PC_BLACK) ? move->coords.src.row : 7 - move->coords.src.row;
    }
    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 &= 0x7;
  if(piece == PC_KING)
  {
    if(abs(move->coords.dst.col - move->coords.src.col) == 2)
      move->score += CASTLE_SCORE;
  }

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

  ascend_required = move_piece(&temp, move->coords.src, move->coords.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->coords.dst, ascend);
        stale = check_stale(&temp, king_pos, -1);
        if(stale == 1)
          move->score = noascend_score + ascend_value(ascend) + 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;
              move_count = legal_moves(&temp, pc_coords, &king_pos, moves);
              if(move_count)
              {
                legal_move_count += move_count;
                lmove.coords.src = pc_coords;
                while(move_count--)
                {
                  lmove.coords.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;
                  }
                }
              }
            }
          }
        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;

unsigned moves_above_score(int32_t score)
{
//  unsigned char row, col;
  unsigned char n;
  unsigned char count;
  unsigned retval = 0;
  struct move *ptr;

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

  for(n=0;n<64;n++)
  {
    count = all_moves[n].move_count;
    ptr = all_moves[n].moves;
    while(count--)
    {
      if(ptr->score >= score)
        retval++;
      ptr++;
    }
  }

  return retval;
}

int ask_resign(void)
{
  int chr;

  game_status->resign_asked = 1;

  while(1)
  {
    chr = show_dialog("Looks like I need more practice. Will you accept my resignation? (y/n)");
    if(chr == 'y' || chr == 'Y')
      return 1;
    else if(chr == 'n' || chr == 'N')
      return 0;
  }
}

#define BESTVAL_LIMIT 1000

struct move do_ai_move(struct coord king_pos, signed char *dontcommit)
{
  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(&board, &max, -1, dontcommit))
    return max;

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

  d = setup.depth1;

  for(a=0, total_moves=bestval_limit2=0, i=0; d<=setup.depth2; d += setup.depth2 - setup.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<<3)+col];
          if(!i)
          {
            piece_moves->move_count = legal_moves(&board, pc_coords, &king_pos, piece_moves->legal_moves);
            if(piece_moves->move_count)
            {
              memset(piece_moves->moves, 0, sizeof(piece_moves->moves));
            }
          }

          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 defined(SDLFUNCS) || defined(DOS)
              if(graphics)
              {
                print_timectrl(1);
                if(playeroutoftime())
                  goto make_move;
              }
#endif
              if(move->score < bestval)
                continue;

              move->coords.src = pc_coords;
              move->coords.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_nostale.score > (-QUEEN_VALUE/2) || s < 2)
                  {
                    if(!graphics) puts("Trying to avoid draw...");
                  }
                  else goto select_stale;
                }
                else select_stale:max_nostale = max;
              }
              else if(setup.blunders && !(rand() % setup.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(setup.depth2 == setup.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, setup.depth2);
    }

  }

  make_move:

#if defined(SDLFUNCS) || defined(DOS)
  if(playeroutoftime())
    return max;
#endif

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

  if(dontcommit)
  {
    *dontcommit = s;
    return max;
  }

#if defined(SDLFUNCS) || defined(DOS)
  if((!setup.white_ai || !setup.black_ai) && setup.enable_clippy && s > 2 && max.score < QUEEN_VALUE)
    if(!game_status->resign_asked && (max.resign = ask_resign()))
      return max;
#endif

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

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

  return max;
}
