/* --------------------------------------------------------------------------
This function is qsort's callback
to sort the moves based on quality
-------------------------------------------------------------------------- */
int cmpgle(const void* elem1, const void* elem2)
{
    if (    (*(struct _piece_order_struct *)elem1).q     >
            (*(struct _piece_order_struct *)elem2).q     )
        return -1;
    else if (    (*(struct _piece_order_struct *)elem1).q     <
                 (*(struct _piece_order_struct *)elem2).q     )
        return 1;
    else
        return 0;
}

/* --------------------------------------------------------------------------
THIS IS THE CHECKERS ENGINE::

b           - is the start board - it will be changed on return to
              reflect the move or moves (in the case of a double jump)
              made
t           - is whos turn it is RED or BLACK
j           - is jump continuation - must be 0
prune_depth - how many levels to recurse to determine what to prune
prune_size  - how many branches of the pruned tree to pursue (note:
              each piece is considered as one branch even though it
              it results in as many as four actual branches
max_depth   - after the tree has been pruned how far should it recurse
              must be greater than the prune_depth

side effects:
            Calls PrintBoard (debugio.cpp)

note: we may wish to use iterative deepening here and base things on time
-------------------------------------------------------------------------- */
long PlayBestMove(BOARD b, int t, int j, int d,
                    int prune_depth, int prune_size, int max_depth)
{
    /* ----- stack variables ----- */
    int  moves_made        =  0;
    int  best_move_type    = -1;
    int  best_move_number  = -1;
    long best_move_quality = -1;
    long alpha_beta_c      = -1;
    int  fBreak            = 0;
    int  i;

    #ifdef DEBUG
    int  jumps_made        =  0;
    #endif

    AssertSz(prune_size <= SQRS_MAX, "me. prune size is way to big");
    AssertSz(prune_size > 1, "how much? prune size is way to small");
    AssertSz(0 == b[0], "b sub zero should equal zero");
    AssertSz(0 == b[SQRS_MAX-1], "b sub MAX-1 should equal zero");

    /* ----- debug ----- */
    if (debug && d < 3) PrintBoard(b,d);

    /* ----- initialize tables and variables ----- */
    depth_maximum = max_depth;
    computer_color = t;
    for (i=0; i<SQRS_MAX; i++)
        {
        piece_order[i].m = (SQUARE) i;
        piece_order[i].q = -1;
        }

    /* ----- seed the random number generator ----- */
    #ifdef DEBUG
    srand( 0 );
    #else
    #ifdef TEST
    srand( 0 );
    #else
    srand( (unsigned)time( NULL ) );
    #endif
    #endif

    /* ----- iterate through all possible moves AT PRUNE_DEPTH ----- */
    if (prune_depth > 0)
        {
        depth_maximum = prune_depth;
        pdebug(stddbgmin,"PlayBestMove d=%d %s(%d)\n",d,__FILE__,__LINE__);
        for (i=0;i<SQRS_MAX; i++)
            {
            int piece;
            piece = (int) piece_order[i].m;
            if (0 == (b[piece] & t)) continue; /* not our piece */
            moves_made += IterateJumps(b,piece,t,d,&best_move_type,&best_move_number,&piece_order[i].q,alpha_beta_c,best_move_quality,&fBreak);
            AssertSz(0 == fBreak,"I must not prune at the root level of the tree");
            #ifdef DEBUG
            jumps_made += moves_made;
            #endif
            }
        if (0 == moves_made /* enforce the must jump rule */
            #ifndef HIGH_PERFORMANCE
                || 0==rConfig.iMustJump
            #endif
            )
            {
            pdebug(stddbgmin,"could not find a jumping move to make %s(%d)\n",__FILE__,__LINE__);
            for (i=0;i<SQRS_MAX; i++)
                {
                int piece;
                piece = (int) piece_order[i].m;
                if (0 == (b[piece] & t)) continue; /* not our piece */
                moves_made += IterateMoves(b,piece,t,d,&best_move_type,&best_move_number,&piece_order[i].q,alpha_beta_c,best_move_quality,&fBreak);
                AssertSz(0 == fBreak,"I ought not prune at the root level of the tree");
                }
            }
        if (0 == moves_made)
            {
            pdebug(stddbgmin,"could not find a move to make (error not handled) %s(%d)\n",__FILE__,__LINE__);
            AssertSz(0, "I give up... why don't you go instead");
            }
        }
    else
        {
        moves_made = 1000;
        prune_size = SQRS_MAX;
        }

    /* ----- if we only have one move to make, dont recurse again ----- */
    if (moves_made > 1)
        {
        /* ----- shuffle the moves ----- */
        for (i=SQRS_MAX-1; i > 0; i--)
            {
            int r;
            struct _piece_order_struct temp;

            /* ----- pick a random interchange ----- */
            r = ((unsigned int)rand()) % ((unsigned int) i);

            /* ----- swap the order ----- */
            temp = piece_order[i];
            piece_order[i] = piece_order[r];
            piece_order[r] = temp;
            }

        /* ----- sort the move table based on the move qualities
                of each move that we tried ----- */
        pdebug(stddbgmin,"sorting... %s(%d)\n",__FILE__,__LINE__);
        qsort(piece_order,SQRS_MAX, sizeof(struct _piece_order_struct), cmpgle);
        pdebug(stddbgmin,"done sorting. %s(%d)\n",__FILE__,__LINE__);


        /* ----- recurse the pruned board to a deeper level ----- */
continue_jump:
        moves_made = 0;
        depth_maximum = max_depth;
        pdebug(stddbgmin,"recursing pruned PlayBestMove d=%d %s(%d)\n",d,__FILE__,__LINE__);
        for (i=0;i<prune_size; i++)
            {
            int piece;
            piece = (int) piece_order[i].m;
            if (0 == (b[piece] & t)) continue; /* not our piece */
            moves_made += IterateJumps(b,piece,t,d,&best_move_type,&best_move_number,&best_move_quality,alpha_beta_c,best_move_quality,&fBreak);
            AssertSz(0 == fBreak,"x2 I must not prune at the root level of the tree");
            #ifdef DEBUG
            jumps_made += moves_made;
            #endif
            }
        if ((0 == moves_made
            #ifndef HIGH_PERFORMANCE
                || 0==rConfig.iMustJump
            #endif
            )&& 0 == j) /* enforce the must jump rule */
            {
            pdebug(stddbgmin,"could not find a jumping move to make %s(%d)\n",__FILE__,__LINE__);
            for (i=0;i<prune_size; i++)
                {
                int piece;
                piece = (int) piece_order[i].m;
                pdebug(stddbgmin,"Next move piece=%d i=%d %s(%d)\n",piece,i,__FILE__,__LINE__);
                if (0 == (b[piece] & t)) continue; /* not our piece */
                moves_made += IterateMoves(b,piece,t,d,&best_move_type,&best_move_number,&best_move_quality,alpha_beta_c,best_move_quality,&fBreak);
                AssertSz(0 == fBreak,"x3 I must not prune at the root level of the tree");
                }
            }
        if (0 == moves_made)
            {
            pdebug(stddbgmin,"could not find a move to make (error ! handled) %s(%d)\n",__FILE__,__LINE__);
            #ifdef DEBUG
            AssertSz(jumps_made, "Your turn.  I mean.  Can I move or something? .. nevermind .. why don't you just go");
            #endif
            }
        }

    /* ----- do the first step of any jumps ----- */
    if (best_move_type > TYPE_BLACK2)
        {
        switch (best_move_type)
            {
            case TYPE_RED_JUMP0   :
                j = red_jump_lut[best_move_number][1]  ;
                piece_order[0].m = (SQUARE) j;
                MakeMoveNoKing_RED_JUMP0(b,best_move_number);
                if (0 != red_jump_lut[best_move_number][4] && !(KING & b[red_jump_lut[best_move_number][1]])) goto no_jump;
                break;

            case TYPE_RED_JUMP2   :
                j = red_jump_lut[best_move_number][3]  ;
                piece_order[0].m = (SQUARE) j;
                MakeMoveNoKing_RED_JUMP2(b,best_move_number);
                if (0 != red_jump_lut[best_move_number][4] && !(KING & b[red_jump_lut[best_move_number][3]])) goto no_jump;
                break;

            case TYPE_BLACK_JUMP0 :
                j = black_jump_lut[best_move_number][1];
                piece_order[0].m = (SQUARE) j;
                MakeMoveNoKing_BLACK_JUMP0(b,best_move_number);
                if (0 != black_jump_lut[best_move_number][4] && !(KING & b[black_jump_lut[best_move_number][1]])) goto no_jump;
                break;

            case TYPE_BLACK_JUMP2 :
                j = black_jump_lut[best_move_number][3];
                piece_order[0].m = (SQUARE) j;
                MakeMoveNoKing_BLACK_JUMP2(b,best_move_number);
                if (0 != black_jump_lut[best_move_number][4] && !(KING & b[black_jump_lut[best_move_number][3]])) goto no_jump;
                break;

            default:
                AssertSz(0, "end case");
                break;
            }

        /* ----- re-initialize ----- */
        prune_size        =  1; /* ! force it to use only the jump destination as a move */
        moves_made        =  0;
        best_move_type    = -1;
        best_move_number  = -1;
        best_move_quality = -1;

        goto continue_jump;
        }
no_jump:

    switch (best_move_type)
        {
        case TYPE_RED0        :
            MakeMove_RED0(b,best_move_number);
            break;

        case TYPE_RED2        :
            MakeMove_RED2(b,best_move_number);
            break;

        case TYPE_BLACK0      :
            MakeMove_BLACK0(b,best_move_number);
            break;

        case TYPE_BLACK2      :
            MakeMove_BLACK2(b,best_move_number);
            break;

        case TYPE_RED_JUMP0   :
        case TYPE_RED_JUMP2   :
        case TYPE_BLACK_JUMP0 :
        case TYPE_BLACK_JUMP2 :
            pdebug(stddbgmin,"skipping a jump move in IO %s(%d)\n",__FILE__,__LINE__);
            break;

        default:
            pdebug(stddbgmin,"printmove did not make the move on the board %s(%d)\n",__FILE__,__LINE__);
            pdebug(stddbgmin,"this could be a jump continuation %s(%d)\n",__FILE__,__LINE__);
            break;
        }

    // make sure everything is kinged (in case we jumped to this point)
    if (RED & b[1]) b[1] |= KING;
    if (RED & b[2]) b[2] |= KING;
    if (RED & b[3]) b[3] |= KING;
    if (RED & b[4]) b[4] |= KING;
    if (BLACK & b[32]) b[32] |= KING;
    if (BLACK & b[31]) b[31] |= KING;
    if (BLACK & b[30]) b[30] |= KING;
    if (BLACK & b[29]) b[29] |= KING;

    /* ----- return to caller ----- */
    return best_move_quality;
}
