// 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 <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

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.num_attacks(dest_square, board.OppositeSide()) == 0)
            // 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 += 40 + (est/16);
        }
        else // en-passant?
            val += 32;
    }
    if (move == Killers1[0] || move == Killers2[0])
        val += 30;
    Board board_copy(board);
    ReversibleMove rmove(board, start_square, dest_square);
    board.MakeMove(rmove);
    val -= Scoring::positional_score(board);
    if (board.CheckStatus() == Board::InCheck)
       val += 25;
    board = board_copy; // undo move
    return val;
}

void 
Move_Ordering::order_moves(Board & board,
                           Move moves[], const int num_moves,
                           const int ply, const Move & best_move)
{
    if (num_moves == 0)
        return;
    if (ply > 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;
        Move tmp;
        for (i = 0; i < num_moves; i++)
        {
            int score = 0;
            const Square dest = moves[i].DestSquare();
            const Square start = moves[j].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();
/**                
                if (Scoring::check_en_prise(board,
                   OppositeColor(p.Color()),
                    p,dest))
                    // piece appears to be en prise
                    score = capture_val;
                else 
                if (capture_val > start_val)
**/
                int 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
            {
                // 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);
       }
    } 
    else 
    {
        // ply == 0

        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);
        delete [] scores;
    }
}

void 
Move_Ordering::clear_killer()
{
    for (int i = 0; i < Constants::MaxPly; i++)
    {
        Killers1[i].MakeNull();
        Killers2[i].MakeNull();   
    }
    if (global_options->use_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)
{
    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()]++;
    }
}

