// Copyright 1994, 1995, 1996 by Jon Dart.  All Rights Reserved.

#include "stdafx.h"
#include "moveord.h"
#include "scoring.h"
#include "rmove.h"
#include "constant.h"
#include "attacke.h"
#include "bearing.h"
#include "util.h"
#include "globals.h"
#include "scoring.h"
#include <iostream.h>

static Move Killers1[Constants::MaxPly];
static Move Killers2[Constants::MaxPly];
static int HistoryW[64][64];
static int HistoryB[64][64];

inline void swap( Move moves[], const int i, const int j)
{
    Move tmp = moves[j];
    moves[j] = moves[i]; 
    moves[i] = tmp;
}

#define SORT_LIMIT 8

// move scoring routine, used only at ply 0
int Move_Ordering::score(Board &board, const Move &move )
{
    int val = 0;
    Square dest_square = move.DestSquare();
    Square start_square = move.StartSquare();
    Piece capture = board[dest_square];
    if (!capture.IsEmpty())
    {
        ExtendedMove emove(board,move);
        if (!board.any_attacks(dest_square, board.OppositeSide()))
            // attack on undefended piece
            val += capture.Value();
        else if (emove.Special() == ExtendedMove::Normal)
        {
            int est = attack_estimate(board,emove);
            if (est > 0)
               val += est;
            else // losing capture
               val += 10 + (est/16);
        }
        else // en-passant?
            val += 32;
    }
    if (move == Killers1[0] || move == Killers2[0])
        val += 30;
    ReversibleMove rmove(board, start_square, dest_square);
    board.MakeMove(rmove);
    if (board.any_attacks(board.KingPos(board.OppositeSide()),board.Side()))
    {
       // move into check, illegal
       val = -Constants::BIG;
    }
    else
    {
       val -= Scoring::positional_score(board);
       if (board.CheckStatus() == Board::InCheck)
          val += 25;
    }
    board.UndoMove(rmove);
    return val;
}

//#pragma optimize("g",off)
int
Move_Ordering::order_moves(Board & board,
                           Move moves[], const int num_moves,
                           const int ply, const Move & best_move)
{
    if (num_moves == 0)
        return 0;
    // Move gainful capture moves and killers to the front
    // of the move list.
    static int scores[Move_Generator::MaxMoves];
    int j = 0, i = 0;
    for (i = 0; i < num_moves; i++)
    {
        int score = 0;
        const Square dest = moves[i].DestSquare();
        const Square start = moves[i].StartSquare();
        const Piece capture = board[dest];

        if (moves[i] == best_move)
        {
            swap(moves,i,0);
            scores[0] = Constants::BIG;
            ++j;
        }        
        else if (!capture.IsEmpty())
        {
            const Piece p(board[dest]);
            int start_val;
            if (board[start].Type() == Piece::King)
                start_val = Piece::Value(Piece::Pawn);
            else
                start_val = board[start].Value();
            int capture_val = capture.Value();
             int gain;
            if (capture_val > start_val && moves[i].PromoteTo() == Piece::Invalid)
               gain = capture_val - start_val;
            else
               gain = attack_estimate(board,
                  ExtendedMove(board,moves[i]));
            if (gain >= 0)
            {
                score = gain;
                if (i != j)
                {
                   swap(moves,i,j);
                }
                scores[j] = score;
                ++j;
            }
        } 
        else if (moves[i].PromoteTo() != Piece::Invalid)
        {
            if (board.any_attacks(moves[i].DestSquare(),
               board.OppositeSide()))
            {
               score = (int)moves[i].PromoteTo();
            }
            else
               score = Piece::Value(moves[i].PromoteTo()) - 64;
            if (score > 0)
            {
                scores[j] = score;
                swap(moves,i,j);
                ++j;
            }
        }
        else
        {
            // not a capture move
            if (moves[i] == Killers1[ply] ||
                moves[i] == Killers2[ply])
                score += 30;
            if (score)
            {
                scores[j] = score;
                swap(moves,i,j);
                ++j;
            }
        }
   }
   if (j > 1)
       sort_moves(moves,scores,j);
   if (global_options->use_history() && num_moves > j)
   {
       for (i = j; i < num_moves; i++)
       {
           if (board.Side() == White)
           {
               scores[i] = HistoryW[moves[i].StartSquare()][moves[i].DestSquare()];
           }
           else
           {
               scores[i] = HistoryB[moves[i].StartSquare()][moves[i].DestSquare()];
           }
       }
       sort_moves(moves+j,scores+j,num_moves-j);
   }
   return num_moves;
}
//#pragma optimize("g",on)

int Move_Ordering::order_ply0_moves( Board &board, Move moves[],
  const int num_moves, const Move &best_move, BOOL &easy)
{
     Scoring::init(board);
     int *scores = new int[num_moves];

     for (int i = 0; i < num_moves; i++)
     {
         if (moves[i] == best_move)
             scores[i] = Constants::BIG;
         else
             scores[i] = score(board,moves[i]);
     }
     sort_moves(moves, scores, num_moves);
     int real_num_moves = num_moves;

     easy = TRUE;
     for (i=num_moves-1; i >=0; --i)
     {
         if (scores[i] == -Constants::BIG)
         {
             --real_num_moves;
         }
         else if (i >0 && scores[0]-100 < scores[i])
             easy = FALSE;
     }
     delete [] scores;
     return real_num_moves;
}

int
Move_Ordering::order_captures(Board & board,
                           Move moves[], const int num_moves,
                           const int ply, const Move & best_move)
{
   static int scores[Move_Generator::MaxMoves];
   int trim = 0;
   for (int i = 0; i < num_moves; i++)
   {
         Square dest = moves[i].DestSquare();
         Square loc = moves[i].StartSquare();
  	 if (!board[dest].IsEmpty())
         {
              int est;
              unsigned start_val;
              if (board[loc].Type() == Piece::King)
                 start_val = Piece::Value(Piece::Pawn);
              else
                  start_val = board[loc].Value();
              if (board[dest].Type() == Piece::King) // king capture
                  est = Constants::BIG;
              else if (board[dest].Value() > start_val &&
                  moves[i].PromoteTo() == Piece::Invalid)
              {
                  est = board[dest].Value() - start_val;
              }
              else
              {
                  est =
                       attack_estimate(board,ExtendedMove(board,
                                   Move(loc,dest)));
              }
              if (est <= 0) ++trim;
              scores[i] = est;
         }
         else if (moves[i].PromoteTo() !=
             Piece::Invalid)
         {
             scores[i] =
                 Piece::Value(moves[i].PromoteTo()) - 64;
         }
         else
         {
             scores[i] = -1;
             ++trim;
         }
   }
   Move_Ordering::sort_moves(moves, scores, num_moves);
   return (board.CheckStatus() == Board::InCheck ?
        num_moves : num_moves - trim);
}

void 
Move_Ordering::clear_killer()
{
    for (int i = 0; i < Constants::MaxPly; i++)
    {
        Killers1[i].MakeNull();
        Killers2[i].MakeNull();   
    }
}

void 
Move_Ordering::clear_history()
{
    memset(HistoryW,'\0',4096*sizeof(int));
    memset(HistoryB,'\0',4096*sizeof(int));
}

void 
Move_Ordering::set_killer(const Board& board, ExtendedMove & move, 
  const unsigned ply, const unsigned depth)
{
    int num_killers = global_options->num_killers();
    if (num_killers > 1)
    {
       Killers2[ply] = Killers1[ply];
    }
    if (num_killers > 0)
    {
        Killers1[ply] = move;
    }
    if (global_options->use_history())
    {
        if (board.Side() == White)
           HistoryW[(int)move.StartSquare()][(int)move.DestSquare()]++;
        else
           HistoryB[(int)move.StartSquare()][(int)move.DestSquare()]++;
    }
}

Move Move_Ordering::get_killer(int ply,int index)
{
    return (index == 0) ? Killers1[ply] : Killers2[ply];
}

void Move_Ordering::update_killers()
{
    // not currently implemented; we just clear the killer lists
    clear_killer();
}


