wpawn/* Graphics Not Rendered in HTML */



wcastle/* Graphics Not Rendered in HTML */




wknight/* Graphics Not Rendered in HTML */





wbishop/* Graphics Not Rendered in HTML */





wqueen/* Graphics Not Rendered in HTML */





wking/* Graphics Not Rendered in HTML */




bpawn/* Graphics Not Rendered in HTML */



bcastle/* Graphics Not Rendered in HTML */




bknight/* Graphics Not Rendered in HTML */





bbishop/* Graphics Not Rendered in HTML */





bqueen/* Graphics Not Rendered in HTML */






bking/* Graphics Not Rendered in HTML */

//These are weights for scoring positions.
#define SHIFT_PIECE_SCORE       4
#define SHIFT_POSSIBLE_MOVES    0

#define P_PAWN      0
#define P_ROOK      1
#define P_KNIGHT    2
#define P_BISHOP    3
#define P_QUEEN     4
#define P_KING      5

U8 *imgs[12] = {<1>, <7>, <2>, <8>, <3>, <9>, <4>, <10>, <5>, <11>, <6>, <12>};

I64 piece_scores[6] = {

    1   << SHIFT_PIECE_SCORE,
    5   << SHIFT_PIECE_SCORE,
    3   << SHIFT_PIECE_SCORE,
    3   << SHIFT_PIECE_SCORE,
    9   << SHIFT_PIECE_SCORE,
    100 << SHIFT_PIECE_SCORE
};

U8 piece_letters[] = "PRNBQK";

class Move
{
    I8  piece, x, y, pad;
    I32 score;
};

class PieceState
{
    I8 player, type, x, y;
};

class GameState
{
    GameState   *next, *last, *parent;
    PieceState   pieces[32];
    I64          cur_player,
                 mv_num,
                 moved_mask,
                 raw_score;     //Positive favors player#0
    Bool         is_human[2];
    Move         potential_en_passant_victim;
};

GameState setup_state = {

    NULL, NULL, NULL,
    {
        {0, P_KING,   0, 4},
        {0, P_QUEEN,  0, 3},
        {0, P_BISHOP, 0, 2},
        {0, P_BISHOP, 0, 5},
        {0, P_KNIGHT, 0, 1},
        {0, P_KNIGHT, 0, 6},
        {0, P_ROOK,   0, 0},
        {0, P_ROOK,   0, 7},

        {0, P_PAWN, 1, 0},
        {0, P_PAWN, 1, 1},
        {0, P_PAWN, 1, 2},
        {0, P_PAWN, 1, 3},
        {0, P_PAWN, 1, 4},
        {0, P_PAWN, 1, 5},
        {0, P_PAWN, 1, 6},
        {0, P_PAWN, 1, 7},

        {1, P_KING,   7, 4},
        {1, P_QUEEN,  7, 3},
        {1, P_BISHOP, 7, 2},
        {1, P_BISHOP, 7, 5},
        {1, P_KNIGHT, 7, 1},
        {1, P_KNIGHT, 7, 6},
        {1, P_ROOK,   7, 0},
        {1, P_ROOK,   7, 7},

        {1, P_PAWN, 6, 0},
        {1, P_PAWN, 6, 1},
        {1, P_PAWN, 6, 2},
        {1, P_PAWN, 6, 3},
        {1, P_PAWN, 6, 4},
        {1, P_PAWN, 6, 5},
        {1, P_PAWN, 6, 6},
        {1, P_PAWN, 6, 7},
    },
    0, 0, 0, 0,
    {TRUE, FALSE},
    {0, -1, -1, 0, 0}};

GameState cur_state;
CDoc     *chess_doc;

U0 GameSnapShot()
{
    GameState *tmpg = MAlloc(sizeof(GameState));

    MemCopy(tmpg, &cur_state, sizeof(GameState));
    QueueInsert(tmpg, cur_state.last);
}

I64 PieceFind(I64 x, I64 y, I64 player)
{
    I64 i;

    for (i = 0; i < 32; i++)
        if (cur_state.pieces[i].player == player && cur_state.pieces[i].x == x && cur_state.pieces[i].y == y)
          return i;

    return -1;
}


#define BD_BORDER   4
#define BD_SIDE     (BD_BORDER + 8 + BD_BORDER)
#define T_BLKED     -1
#define T_PLAYER0   0
#define T_PLAYER1   1
#define T_CLR       2

U0 BrdFill(I8 *brd, PieceState *pieces)
{
    I64 i;

    MemSet(brd, T_BLKED, sizeof(I8) * BD_SIDE * BD_SIDE);

    for (i = BD_BORDER; i < 8 + BD_BORDER; i++)
        brd[BD_SIDE * i + BD_BORDER](I64) = 0x0202020202020202;
#assert T_CLR == 2

    for (i = 0; i < 32; i++)
        if (!pieces[i].player)
            brd[BD_SIDE * (BD_BORDER + pieces[i].y) + BD_BORDER + pieces[i].x] = T_PLAYER0;
        else if (pieces[i].player == 1)
            brd[BD_SIDE * (BD_BORDER + pieces[i].y) + BD_BORDER + pieces[i].x] = T_PLAYER1;
}

I8 BrdPeek(I8 *brd, I64 x, I64 y)
{
    return brd[(y + BD_BORDER) * BD_SIDE + BD_BORDER + x];
}


I64 knight_x_offsets[8]  = {-1,  1,  2, 2, -1, 1, -2, -2},
    knight_y_offsets[8]  = {-2, -2, -1, 1,  2, 2, -1,  1},
    bishop_x_offsets3[4] = {-1,  1, -1, 1},
    bishop_y_offsets3[4] = {-1, -1,  1, 1};

I64 PieceMoves(GameState *state, I64 piece_num, Move *mvs)
{
    PieceState  *p = state->pieces, *cur_p = &p[piece_num];
    I64          res = 0, i, j, k, x, y, xx = cur_p->x, yy = cur_p->y;
    I8           brd[BD_SIDE * BD_SIDE * sizeof(I8)];

    BrdFill(brd, p);
    switch [state->pieces[piece_num].type]
    {
        case P_PAWN:
            if (!state->cur_player)
            {
                if (BrdPeek(brd, xx + 1, yy) == T_CLR)
                {
                    mvs[res].x = xx + 1;
                    mvs[res].y = yy;
                    res++;
                    if (xx == 1 && BrdPeek(brd, xx + 2, yy) == T_CLR)
                    {
                        mvs[res].x = xx + 2;
                        mvs[res].y = yy;
                        res++;
                    }
                }
                if (BrdPeek(brd, xx + 1, yy - 1) == T_PLAYER1 ||
                    state->potential_en_passant_victim.x == xx &&
                    state->potential_en_passant_victim.y == yy - 1)
                {
                    mvs[res].x = xx + 1;
                    mvs[res].y = yy - 1;
                    res++;
                }
                if (BrdPeek(brd, xx + 1, yy + 1) == T_PLAYER1 ||
                    state->potential_en_passant_victim.x == xx &&
                    state->potential_en_passant_victim.y == yy + 1)
                {
                    mvs[res].x = xx + 1;
                    mvs[res].y = yy + 1;
                    res++;
                }
            }
            else
            {
                if (BrdPeek(brd, xx - 1, yy) == T_CLR)
                {
                    mvs[res].x = xx - 1;
                    mvs[res].y = yy;
                    res++;
                    if (xx == 6 && BrdPeek(brd, xx - 2, yy) == T_CLR)
                    {
                        mvs[res].x = xx - 2;
                        mvs[res].y = yy;
                        res++;
                    }
                }
                if (!BrdPeek(brd, xx - 1, yy - 1) ||
                    state->potential_en_passant_victim.x == xx &&
                    state->potential_en_passant_victim.y == yy - 1)
                {
                    mvs[res].x = xx - 1;
                    mvs[res].y = yy - 1;
                    res++;
                }
                if (!BrdPeek(brd,xx-1,yy+1) ||
                    state->potential_en_passant_victim.x==xx &&
                    state->potential_en_passant_victim.y==yy+1)
                {
                    mvs[res].x = xx - 1;
                    mvs[res].y = yy + 1;
                    res++;
                }
            }
            break;

        case P_ROOK:
            for (j = 0; j < 4; j++)
            {
                x = xx;
                y = yy;
                for (i = 1; i < 8; i++)
                {
                    x += gr_x_offsets2[j];
                    y += gr_y_offsets2[j];
                    k = BrdPeek(brd, x, y);
                    if (k == T_CLR || k == cur_p->player ^ 1)
                    {
                        mvs[res].x = x;
                        mvs[res].y = y;
                        res++;
                        if (k != T_CLR)
                            break;
                    }
                    else
                        break;
                }
            }
            break;

        case P_KNIGHT:
            for (j = 0; j < 8; j++)
            {
                x = xx + knight_x_offsets[j];
                y = yy + knight_y_offsets[j];
                k = BrdPeek(brd, x, y);
                if (k == T_CLR || k == cur_p->player ^ 1)
                {
                    mvs[res].x = x;
                    mvs[res].y = y;
                    res++;
                }
            }
            break;

        case P_BISHOP:
            for (j = 0; j < 4; j++)
            {
                x = xx;
                y = yy;
                for (i = 1; i < 8; i++)
                {
                    x += bishop_x_offsets3[j];
                    y += bishop_y_offsets3[j];
                    k = BrdPeek(brd, x, y);
                    if (k == T_CLR || k == cur_p->player ^ 1)
                    {
                        mvs[res].x = x;
                        mvs[res].y = y;
                        res++;
                        if (k != T_CLR)
                            break;
                    }
                    else
                        break;
                }
            }
            break;

        case P_QUEEN:
            for (j = 0; j < 8; j++)
            {
                x = xx;
                y = yy;
                for (i = 1; i < 8; i++)
                {
                    x += gr_x_offsets[j];
                    y += gr_y_offsets[j];
                    k = BrdPeek(brd, x, y);
                    if (k == T_CLR || k == cur_p->player ^ 1)
                    {
                        mvs[res].x = x;
                        mvs[res].y = y;
                        res++;
                        if (k != T_CLR)
                            break;
                    }
                    else
                        break;
                }
            }
            break;

        case P_KING:
            for (j = 0; j < 8; j++)
            {
                x = xx + gr_x_offsets[j];
                y = yy + gr_y_offsets[j];
                k = BrdPeek(brd, x, y);
                if (k == T_CLR || k == cur_p->player ^ 1)
                {
                    mvs[res].x = x;
                    mvs[res].y = y;
                    res++;
                }
            }
            if (!Bt(&state->moved_mask, piece_num))
            {
                if (!state->cur_player)
                {
                    if (state->pieces[0].player >= 0 &&
                        !Bt(&state->moved_mask, 0)   &&
                        BrdPeek(brd, 0, 1) == T_CLR  &&
                        BrdPeek(brd, 0, 2) == T_CLR  &&
                        BrdPeek(brd, 0, 3) == T_CLR)
                    {
                        mvs[res].x = 0;
                        mvs[res].y = 2;
                        res++;
                    }
                    if (state->pieces[7].player >= 0 &&
                        !Bt(&state->moved_mask, 7)   &&
                        BrdPeek(brd, 0, 6) == T_CLR  &&
                        BrdPeek(brd, 0, 5) == T_CLR)
                    {
                        mvs[res].x = 0;
                        mvs[res].y = 6;
                        res++;
                    }
                }
                else
                {
                    if (state->pieces[24].player >= 0 &&
                        !Bt(&state->moved_mask, 24)   &&
                        BrdPeek(brd, 7, 1) == T_CLR   &&
                        BrdPeek(brd, 7, 2) == T_CLR   &&
                        BrdPeek(brd, 7, 3) == T_CLR)
                    {
                        mvs[res].x = 7;
                        mvs[res].y = 2;
                        res++;
                    }
                    if (state->pieces[31].player >= 0 &&
                        !Bt(&state->moved_mask, 31)   &&
                        BrdPeek(brd, 7, 6) == T_CLR   &&
                        BrdPeek(brd, 7, 5) == T_CLR)
                    {
                        mvs[res].x = 7;
                        mvs[res].y = 6;
                        res++;
                    }
                }
            }
            break;
    }
    for (i = 0; i < res; i++)
        mvs[i].piece = piece_num;

    return res;
}

Bool SqrInChk(GameState *state, I64 x, I64 y)
{
    I64 i, j, mv_cnt;

    Move mvs[32];
    for (i = 0; i < 32; i++)
        if (state->pieces[i].player == state->cur_player)
        {
            mv_cnt = PieceMoves(state, i, mvs);
            for (j = 0; j < mv_cnt; j++)
                if (mvs[j].x == x && mvs[j].y == y)
                    return TRUE;
        }

    return FALSE;
}

I64 PopUpPiece(I64 player)
{
    U8    buf[STR_LEN];
    I64   i;
    CDoc *doc;

    do
    {
        doc = DocNew;
        DocPrint(doc, "\n\n\n\n\n...");
        for (i = P_ROOK; i <= P_QUEEN; i++)
        {
            StrPrint(buf, "$SP+LE+X,\"\",%d,%%d$....", i);
            DocSprite(doc, imgs[2 * i + player], buf);
        }
        DocPrint(doc, "\n\n\n");
        i = PopUpMenu(doc);
        DocDel(doc);
    }
    while (i < 0);

    return i;
}

CDC *brd_dc;
I64  cx, cy, cx2, cy2, g, cursor_x, cursor_y, cur_mv_cnt, *s2w, *w2s;
Move cur_mvs[32], last_mv;
Bool ai_thinking, game_over;

extern I64 MoveCnt(GameState *state);

Bool MoveDo(GameState *state, I64 piece_num, I64 x2, I64 y2, Bool final)
{ //returns TRUE if killed a piece.
    PieceState *p = &state->pieces[piece_num];
    I64         j, ty = -1, score_adjust = 0, x1 = p->x, y1 = p->y;

    j = PieceFind(x2, y2, state->cur_player ^ 1);
    if (j >= 0)
    {
        state->pieces[j].player -= 2; //dead
        ty = state->pieces[j].type;
        score_adjust = piece_scores[ty];
    }
    else if (p->type == P_PAWN && AbsI64(y1 - y2) == 1)
    { //en passant?
        if (!state->cur_player)
            j = PieceFind(x2 - 1, y2, 1);
        else
            j = PieceFind(x2 + 1, y2, 0);
        if (j >= 0)
        {
            state->pieces[j].player -= 2; //dead
            ty = state->pieces[j].type;
            score_adjust = piece_scores[ty];
        }
    }
    if (p->type == P_PAWN && AbsI64(x1 - x2) == 2)
    {
        state->potential_en_passant_victim.x = x2;
        state->potential_en_passant_victim.y = y2;
    }
    else
    {
        state->potential_en_passant_victim.x = -1;
        state->potential_en_passant_victim.y = -1;
    }
    if (p->type == P_PAWN && (!x2 || x2 == 7))
    {
        if (!state->is_human[state->cur_player] || !final)
            p->type = P_QUEEN;
        else
            p->type = PopUpPiece(state->cur_player);
        score_adjust += piece_scores[p->type] - piece_scores[P_PAWN];
    }
    else if (p->type == P_KING && AbsI64(y1 - y2) == 2)
    { //castling?
        if (!state->cur_player)
        {
            if (y2 - y1 < 0)
                state->pieces[6].y = y2 + 1;
            else
                state->pieces[7].y = y2 - 1;
        }
        else
        {
            if (y2 - y1 < 0)
                state->pieces[22].y = y2 + 1;
            else
                state->pieces[23].y = y2 - 1;
        }
    }
    p->x = x2;
    p->y = y2;
    LBts(&state->moved_mask, piece_num);
    if (!state->cur_player)
        state->raw_score += score_adjust;
    else
        state->raw_score -= score_adjust;
    state->cur_player ^= 1;
    state->mv_num++;
    if (final)
    {
        last_mv.x = x2;
        last_mv.y = y2;
        DocPrint(chess_doc, "%02d)", state->mv_num);
        if (state->cur_player)
            DocPrint(chess_doc, "$FG,LTGRAY$");
        else
            DocPrint(chess_doc, "$FG,BLACK");
        DocPrint(chess_doc, "%c:%c%c-%c%c", piece_letters[p->type], y1 + 'A', x1 + '1', y2 + 'A', x2 + '1');
        if (ty >= 0)
        {
            if (state->cur_player)
                DocPrint(chess_doc, "$FG$*$FG,BLACK$");
            else
                DocPrint(chess_doc, "$FG$*$FG,LTGRAY$");
            DocPrint(chess_doc, "%c", piece_letters[ty]);
        }
        DocPrint(chess_doc, "$FG$\n");
        if (!MoveCnt(&cur_state))
        {
            DocPrint(chess_doc, "$FG,RED$Game Over$FG$\n");
            game_over = TRUE;
        }
        GameSnapShot;
  }

  return ToBool(score_adjust);
}

I64 MoveFind(I64 x, I64 y, Move *mvs, I64 mv_cnt)
{
    I64 i;

    for (i = 0; i < mv_cnt; i++)
        if (mvs[i].x == x && mvs[i].y == y)
            return i;

    return -1;
}

Bool HumanMove(GameState *state, I64 piece_num, Move *mvs, I64 mv_cnt, I64 x, I64 y)
{
    if (piece_num >= 0 && MoveFind(x, y, mvs, mv_cnt) >= 0)
    {
        MoveDo(state, piece_num, x, y, TRUE);
        return TRUE;
    }
    else
        return FALSE;
}

I64 ChkPieceMoves(GameState *state, I64 piece_num, Move *src, I64 mv_cnt)
{ //Goes through moves lst for one piece, eliminating invalid moves.
    I64          k, cnt = mv_cnt, res = 0, x, y, dy;
    GameState    state2, *tmpg;
    Move        *dst = src;

    if (state->cur_player) //Find King
        k = 16;
    else
        k = 0;
    while (cnt--)
    {
        MemCopy(&state2, state, sizeof(GameState));
        MoveDo(&state2, piece_num, src->x, src->y, FALSE);

        //Don't repeat board position
        tmpg = cur_state.next;
        while (tmpg != &cur_state)
        {
            if (!MemCompare(tmpg->pieces, state2.pieces, sizeof(PieceState) * 32))
                goto cm_skip;
            tmpg = tmpg->next;
        }

        if (piece_num == k)
        {  //If king is moving, check his new location.
            x = state2.pieces[k].x;
            y = state2.pieces[k].y;
            dy = y - state->pieces[k].y;
            if (AbsI64(dy) == 2 && /*castling?*/ (SqrInChk(&state2, x, y - dy) || SqrInChk(&state2, x, y - SignI64(dy))))
                goto cm_skip; // This really needs to go into PieceMoves
        }
        else
        {
          x = state->pieces[k].x;
          y = state->pieces[k].y;
        }
        if (!SqrInChk(&state2, x, y))
        {
            MemCopy(dst++, src, sizeof(Move));
            res++;
        }
cm_skip:
        src++;
    }

    return res;
}

I64 MoveCnt(GameState *state)
{
    I64  i, res = 0, mv_cnt;
    Move mvs[32];

    for (i = 0; i < 32; i++)
        if (state->pieces[i].player == state->cur_player)
        {
            mv_cnt = PieceMoves(state, i, mvs);
            mv_cnt = ChkPieceMoves(state, i, mvs,mv_cnt);
            res += mv_cnt;
        }
    return res;
}


U0 DrawIt(CTask *task, CDC *dc)
{
    I64          i, x, y, z, k0, k1;
    PieceState  *s = cur_state.pieces;

    brd_dc->flags |= DCF_NO_TRANSPARENTS;
    GrBlot(dc, cx - cx2, cy - cy2, brd_dc);

    task->text_attr = YELLOW << 4;
    dc->x = cx;
    dc->y = cy;

    dc->flags |= DCF_TRANSFORMATION;
    MemCopy(dc->r, w2s, sizeof(I64) * 16);

    if (ai_thinking)
    {
        if (cur_state.cur_player)
            dc->color = BLACK;
        else
            dc->color = WHITE;
        if (Blink)
            GrPrint(dc, -11 * FONT_WIDTH / 2, 2 * FONT_HEIGHT - cy, "Thinking...");
    }

    dc->color = RED;
    for (i = 0; i < cur_mv_cnt; i++)
        GrRect3(dc, (cur_mvs[i].x - 4) * g + g / 4, (cur_mvs[i].y - 4) * g + g / 4, 0, g / 2, g / 2);

    dc->color = LTRED;
    dc->thick = 2;
    GrBorder(dc, (cursor_x - 4) * g + 2, (cursor_y - 4) * g + 2, (cursor_x - 3) * g - 1, (cursor_y - 3) * g - 1);

    DCDepthBufAlloc(dc);
    k0 = k1 = 0;
    for (i = 0; i < 32; i++,s++)
    {
        if (s->player >= 0)
        { //Alive?
            if (s->x != last_mv.x || s->y != last_mv.y || Blink)
            {
                x = g / 2 + (s->x - 4) * g;
                y = g / 2 + (s->y - 4) * g;
                z = 0;
                DCTransform(dc, &x, &y, &z);
                z = GR_Z_ALL + s->x - s->y;

                dc->flags &= ~DCF_TRANSFORMATION;
                Sprite3(dc, x, y, z, imgs[s->type * 2 + s->player]);
                dc->flags |= DCF_TRANSFORMATION;
            }
        }
        else
        { //Dead
            if (s->player == -2)
            {
                if (k1 < 8)
                {
                    x = cx + 100 + k1++ * g / 2;
                    y = cy - 180;
                }
                else
                {
                    x = cx + 100 + (k1++ - 8) * g / 2;
                    y = cy - 130;
                }
                z = 0;
            }
            else
            {
                if (k0 < 8)
                {
                    x = cx + 100 + k0++ * g / 2;
                    y = cy + 220;
                }
                else
                {
                    x = cx + 100 + (k0++ - 8) * g / 2;
                    y = cy + 170;
                }
                z = 0;
            }
            dc->flags &= ~DCF_TRANSFORMATION;
            Sprite3(dc, x, y, z, imgs[s->type * 2 + s->player + 2]);
            dc->flags |= DCF_TRANSFORMATION;
        }
    }
    if (game_over && Blink)
    {
        dc->color = LTRED;
        GrPrint3(dc, -9 * FONT_WIDTH / 2, -FONT_HEIGHT / 2, 0, "Game Over");
    }
}

#define DEPTH   -5

CDC *BrdNew()
{
    I64  i, j, k, w = Fs->pix_width, h = Fs->pix_height;
    CDC *dc;

    g = 42;
    cx = w / 2 + 40;
    cy = h / 2;

    w = Sqrt(2.0) * (8 * g + 2 * FONT_WIDTH);
    h = 8 * g + 2 * FONT_HEIGHT;
    cx2 = w / 2;
    cy2 = h / 2;

    dc = DCNew(w, h);
    dc->x = cx2;
    dc->y = cy2;

    dc->flags |= DCF_TRANSFORMATION;
    MemCopy(dc->r, w2s, sizeof(I64) * 16);

    dc->color = YELLOW;
    GrRect(dc, 0, 0, w, h);

    dc->color = BLACK;
    for (i = -4; i < 4; i++)
    {
        GrPrint3(dc, -4 * g - 4 - FONT_WIDTH,   i * g + g / 2 - 4,          0, "%C", i + 4 + 'A');
        GrPrint3(dc, 4 * g + 14,                i * g + g / 2 - 4,          0, "%C", i + 4 + 'A');
        GrPrint3(dc, i * g + g / 2 + 6,         -4 * g - 12 - FONT_HEIGHT,  0, "%C", i + 4 + '1');
        GrPrint3(dc, i * g + g / 2 - 2,         4 * g + 9,                  0, "%C", i + 4 + '1');
    }

    dc->thick = 1;
    dc->color = BLACK;
    for (i = -4; i <= 4; i++)
    {
        GrLine3(dc, -4 * g,  i * g, 0,  4 * g, i * g, 0);
        GrLine3(dc,  i * g, -4 * g, 0,  i * g, 4 * g, 0);
        GrLine3(dc, -4 * g,  i * g, 0, -4 * g, i * g, DEPTH);
        GrLine3(dc,  i * g,  4 * g, 0,  i * g, 4 * g, DEPTH);
    }
    GrLine3(dc, -4 * g, 4 * g, DEPTH, -4 * g, -4 * g, DEPTH);
    GrLine3(dc, -4 * g, 4*  g, DEPTH,  4 * g, 4  * g, DEPTH);

    k = 0;
    for (j = -4; j < 4; j++)
    {
        for (i = -4; i < 4; i++)
        {
            if ((i + j) & 1)
                dc->color = WHITE;
            else
                dc->color = DKGRAY;
            GrFloodFill3(dc, i * g + g / 2, j * g + g / 2, 0);
        }
        if (j & 1)
            dc->color = DKGRAY;
        else
            dc->color = WHITE;
        GrFloodFill3(dc, -4 * g, j * g + g / 2, DEPTH / 2);
        if (j & 1)
            dc->color = WHITE;
        else
            dc->color = DKGRAY;
        GrFloodFill3(dc, j * g + g / 2, 4 * g, DEPTH / 2);
    }

    return dc;
}

I64 AIStateScore(GameState *state, I64 player)
{
    if (player)
        return -state->raw_score;
    else
        return  state->raw_score;
}

I64 move_depth, kill_depth;

I64 AIMove2(GameState *state, I64 depth)
{
    I64         i, possible_moves = 0, best_piece, score,
                best_score = I64_MIN, mv_cnt, score_adjust;
    Move        best_mv, mvs[32],*ptr;
    GameState   state2;
    Bool        first = TRUE;

    if (UnusedStack < 0x1000)
    { //Shouldn't happen
        DocPrint(chess_doc, "StkOverflow\n");
        return AIStateScore(state, state->cur_player);
    }

    for (i = 0; i < 32; i++)
        if (state->pieces[i].player == state->cur_player)
        {
            mv_cnt = PieceMoves(state, i, mvs);
            possible_moves += mv_cnt;
            ptr = mvs;
            while (mv_cnt--)
            {
                MemCopy(&state2, state, sizeof(GameState));
                state2.parent = state;
                if (MoveDo(&state2, i, ptr->x, ptr->y, FALSE) && depth < kill_depth || depth < move_depth)
                    score  =-AIMove2(&state2, depth + 1);
                else
                {
                    score = AIStateScore(&state2, state->cur_player);
                    if (score_adjust = AbsI64(state2.raw_score - state->raw_score))
                        score += score_adjust - piece_scores[state->pieces[i].type];
                }
                if (first || score>best_score)
                {
                    best_score = score;
                    best_piece = i;
                    MemCopy(&best_mv, ptr, sizeof(Move));
                    first = FALSE;
                }
                ptr++;
            }
        }
    if (!first)
        MoveDo(state, best_piece, best_mv.x, best_mv.y, FALSE);

    return best_score + possible_moves << SHIFT_POSSIBLE_MOVES;
}

class ChessJob
{
    GameState   *state;
    Move        *mv;
};

U0 AIMove1(ChessJob *cj)
{
    GameState    state2;
    Move        *ptr = cj->mv;

    MemCopy(&state2, cj->state, sizeof(GameState));
    state2.parent = cj->state;
    MoveDo(&state2, ptr->piece, ptr->x, ptr->y, FALSE);
    ptr->score = -AIMove2(&state2, 1);
    Free(cj);
}

I64 MoveCmp(Move *e1, Move *e2)
{
    if (e1->score < e2->score)
        return 1;
    else if (e1->score == e2->score)
        return 0;
    else
        return -1;
}

U0 AIMove(GameState *state)
{
    I64          i, cnt = 0, mv_cnt, best_score;
    Move         mvs[32], *ptr, *ptr2;
    ChessJob    *cj;

    if (cnt = MoveCnt(state))
    {//Collect AI possible valid moves
        ptr = ptr2 = MAlloc(cnt * sizeof(Move));
        for (i = 0; i < 32; i++)
            if (state->pieces[i].player == state->cur_player)
            {
                mv_cnt = PieceMoves(state, i, mvs);
                mv_cnt = ChkPieceMoves(state, i, mvs, mv_cnt);
                MemCopy(ptr, mvs, mv_cnt * sizeof(Move));
                ptr += mv_cnt;
            }

            //Dispatch to MP scoring all moves
        for (i = 0, ptr = ptr2; i < cnt; i++, ptr++)
        {
            ptr->score = -I32_MAX;
            cj = MAlloc(sizeof(ChessJob));
            cj->state = state;
            cj->mv    = ptr;
            if (mp_count > 1)
                JobQueue(&AIMove1, cj, i % (mp_count - 1));
            else
            {
                AIMove1(cj);
                Yield;
            }
        }

        //Wait for finish
        for (i = 0, ptr = ptr2; i < cnt; i++, ptr++)
            while (ptr->score == -I32_MAX)
                Yield;
        QuickSort(ptr2, cnt, sizeof(Move), &MoveCmp);

        //Cnt ties for first
        best_score = ptr2->score;
        mv_cnt = 0;
        ptr = ptr2;
        while (mv_cnt < cnt && ptr->score == best_score)
        {
            mv_cnt++;
            ptr++;
        }

        ptr = ptr2 + RandU16 % mv_cnt;
        MoveDo(state, ptr->piece, ptr->x, ptr->y, TRUE);
        Free(ptr2);
    }
}

U0 Init()
{
    chess_doc = DocPut;
    DocClear;
    "$FD,BLACK$$BG,YELLOW$";
    game_over = FALSE;
    ai_thinking = FALSE;
    move_depth = 3;
    kill_depth = 5;
    cur_mv_cnt = 0;
    last_mv.x = -1;
    last_mv.y = -1;
    MemCopy(&cur_state, &setup_state, sizeof(cur_state));
    QueueInit(&cur_state);
    cur_state.is_human[0] = PopUpNoYes("$FG,BLACK$WHITE is human?");
    cur_state.is_human[1] = PopUpNoYes("$FG,BLACK$BLACK is human?");

    GameSnapShot;
}

U0 CleanUp()
{
    QueueDel(&cur_state, TRUE);
}

U0 SetMouseCursor()
{
    I64 x = (cursor_x - 4) * g + g / 2,
        y = (cursor_y - 4) * g + g / 2,
        z = 0;

    Mat4x4MulXYZ(w2s, &x, &y, &z);
    x += cx + Fs->pix_left + Fs->scroll_x;
    y += cy + Fs->pix_top  + Fs->scroll_y;
    if (mouse.pos.x != x || mouse.pos.y != y)
        MouseSet(x, y);
}

U0 SetKbdCursor(I64 x, I64 y)
{
    I64 z;

    x -= cx;
    y -= cy;
    z = y;
    Mat4x4MulXYZ(s2w, &x, &y, &z);
    cursor_x = ClampI64((x + 4 * g) / g, 0, 7);
    cursor_y = ClampI64((y + 4 * g) / g, 0, 7);
}

U0 Chess()
{
    I64 arg1, arg2, cur_piece;

    SettingsPush; //See SettingsPush
    AutoComplete;
    WinBorder;
    WinMax;
    Init;

    MenuPush(
        "File {"
        "  Abort(,CH_SHIFT_ESC);"
        "  Exit(,CH_ESC);"
        "}"
        "Play {"
        "  Move(,CH_SPACE);"
        "  Restart(,'\n');"
        "}");


    Fs->win_inhibit = WIG_TASK_DEFAULT - WIF_SELF_FOCUS - WIF_SELF_BORDER - WIF_FOCUS_TASK_MENU;

    cursor_x = cursor_y = 3;

    w2s = Mat4x4IdentNew; //World-to-Scrn
    Mat4x4RotZ(w2s, -pi / 4);
    Mat4x4RotX(w2s,  pi / 4);

    s2w = Mat4x4IdentNew; //Scrn-to-World
    Mat4x4RotX(s2w, -pi / 4);
    Mat4x4RotZ(s2w,  pi / 4);

    brd_dc = BrdNew;

    SetMouseCursor;

    PaletteSetLight(FALSE);
    Fs->draw_it = &DrawIt;

    try
    {
        while (TRUE)
        {
            switch (MessageScan(&arg1, &arg2, 1 << MESSAGE_KEY_DOWN  +
                                              1 << MESSAGE_KEY_UP    +
                                              1 << MESSAGE_MS_L_DOWN +
                                              1 << MESSAGE_MS_L_UP   +
                                              1 << MESSAGE_MS_MOVE))
            {
                case 0:
                    if (!game_over && !cur_state.is_human[cur_state.cur_player])
                    {
                        ai_thinking = TRUE;
                        AIMove(&cur_state);
                        ai_thinking = FALSE;
                        Beep;
                    }
                    Refresh;
                    break;

                case MESSAGE_MS_MOVE:
                    SetKbdCursor(arg1, arg2);
                    break;

                case MESSAGE_MS_L_DOWN:
                    SetKbdCursor(arg1, arg2);
ch_mv_start:
                    if (!game_over && cur_state.is_human[cur_state.cur_player])
                    {
                        cur_piece = PieceFind(cursor_x, cursor_y, cur_state.cur_player);
                        if (cur_piece >= 0)
                        {
                            cur_mv_cnt = PieceMoves(&cur_state, cur_piece, cur_mvs);
                            cur_mv_cnt = ChkPieceMoves(&cur_state, cur_piece, cur_mvs, cur_mv_cnt);
                        }
                        else
                            cur_mv_cnt = 0;
                    }
                    break;

                case MESSAGE_MS_L_UP:
                    SetKbdCursor(arg1, arg2);
ch_mv_end:
                    if (!game_over && cur_state.is_human[cur_state.cur_player])
                    {
                        if (cur_piece >= 0 && HumanMove(&cur_state, cur_piece, cur_mvs, cur_mv_cnt, cursor_x, cursor_y))
                        {
                            Beep(22);
                            cur_piece = -1;
                            cur_mv_cnt = 0;
                        }
                        else
                        {
                            cur_piece = -1;
                            cur_mv_cnt = 0;
                        }
                    }
                    break;

                case MESSAGE_KEY_UP:
                    if (arg1 == CH_SPACE)
                        goto ch_mv_end;
                    break;

                case MESSAGE_KEY_DOWN:
                    switch (arg1)
                    {
                        case '\n':
                            CleanUp;
                            Init;
                            break;

                        case CH_ESC:
                        case CH_SHIFT_ESC:
                            goto ch_done;
                        case CH_SPACE:
                            goto ch_mv_start;
                        case 0:
                            switch (arg2.u8[0])
                            {
                                case SC_CURSOR_RIGHT:
                                    cursor_y = ClampI64(cursor_y + 1, 0, 7);
                                    SetMouseCursor;
                                    break;

                                case SC_CURSOR_LEFT:
                                    cursor_y = ClampI64(cursor_y - 1, 0, 7);
                                    SetMouseCursor;
                                    break;

                                case SC_CURSOR_DOWN:
                                    cursor_x = ClampI64(cursor_x - 1, 0, 7);
                                    SetMouseCursor;
                                    break;

                                case SC_CURSOR_UP:
                                    cursor_x = ClampI64(cursor_x + 1, 0, 7);
                                    SetMouseCursor;
                                    break;
                            }
                    }
                    break;
            }
        }
ch_done:
    }
    catch
        PutExcept;
    DocClear;
    SettingsPop;
    MenuPop;
    DCDel(brd_dc);
    Free(s2w);
    Free(w2s);
    CleanUp;
}

Chess;

U0 DoChess()
{
    I64     i;
    CTask  *task = User;

    XTalk(task, "#include \"::/Demo/Games/Chess\";\n");
    BirthWait(&task->popup_task);
    TaskWait(task->popup_task);
    MessagePostWait(task->popup_task, MESSAGE_KEY_DOWN_UP, CH_SPACE, 0);
    Sleep(100);
    BirthWait(&task->popup_task);
    TaskWait(task->popup_task);
    MessagePostWait(task->popup_task, MESSAGE_KEY_DOWN_UP, CH_SPACE, 0);
    Sleep(100);
    for (i = 0; i < 8; i++)
    {
        MessagePostWait(task, MESSAGE_KEY_DOWN_UP, 0, SC_CURSOR_DOWN);
        MessagePostWait(task, MESSAGE_KEY_DOWN_UP, 0, SC_CURSOR_RIGHT);
        Sleep(25);
    }
    MessagePostWait(task, MESSAGE_KEY_DOWN_UP, 0, SC_CURSOR_UP);
    Sleep(250);
    MessagePostWait(task, MESSAGE_KEY_DOWN, CH_SPACE, 0);
    MessagePostWait(task, MESSAGE_KEY_DOWN_UP, 0, SC_CURSOR_UP);
    Sleep(250);
    MessagePostWait(task, MESSAGE_KEY_DOWN_UP, 0, SC_CURSOR_UP);
    MessagePostWait(task, MESSAGE_KEY_UP, CH_SPACE, 0);
    Sleep(1000);
    MessagePostWait(task, MESSAGE_KEY_DOWN_UP, CH_SHIFT_ESC, 0);
    DeathWait(&task, TRUE);
}


Chess/* Graphics Not Rendered in HTML */