dotd-2.3.7/area/
dotd-2.3.7/clans/
dotd-2.3.7/classes/
dotd-2.3.7/councils/
dotd-2.3.7/deity/
dotd-2.3.7/dict/
dotd-2.3.7/doc/mudprogs/
dotd-2.3.7/player/a/
dotd-2.3.7/player/g/
/******************************************************
 Desolation of the Dragon MUD II
 (C) 1997-2003  Jesse DeFer
 http://www.dotd.com  dotd@dotd.com
 ******************************************************/

/*
 * Chess for SMAUG 1.x          http://dotd.com
 * (C) Jesse DeFer, dotd@dotd.com, dotd.com 4000
 *
 * Installation instructions:
 * 1.  Copy the chess source to your SMAUG src dir
 * 2.  Add it to your makefile to be compiled/linked with SMAUG
 * 3.  Add do_smaug_chess to mud.h, tables.c, and commands.dat
 * 4.  Update SC_COMMANDNAME to the same name you created your command as
 * 5.  Include chess.h in mud.h, below the typedef for bool
 * 6.  Add 'GAME_BOARD_DATA *game_board;' to your pcdata struct (mud.h)
 * 7.  Add free_game(ch->pcdata->game_board); to db.c, free_char(),
 *     before DISPOSE( ch->pcdata );
 * 8.  Add ch->pcdata->game_board = NULL; to save.c (not very important),
 *     to where the rest of the pcdata structures get initialized
 * 9.  Check the defines after these comments
 * 10. Add lines 2-4 of this file to HELP CHESS (the copyright)
 *
 * IMC Installation instructions:
 * AntiFreeze CL-2 and later:
 * 1. init_chess(); to the mud's boot sequence (boot_db is a good choice)
 * Previous to AntiFreeze CL-1:
 * 1. Paste the following into imc.c after "tell" in imc_recv()
 else if (!strcasecmp(p->type, "chess"))
 {
 void imc_recv_chess(imc_char_data *from, PACKET *p);
 imc_recv_chess(&d, p);
 }
 * 2. Uncomment the #define USE_IMC below
 *
 * NOTE: The IMC code doesn't support castling or pawn promotion yet.
 */

/*
 * Todo: add 3 move repitition draw
 *       castling over imc
 */

/* Changes:
 * 0.23:
 *      only show CVS ID to imms, it's just spam for morts
 *      add _syntax argument so we don't duplicate the syntax string
 * 0.24:
 *      added castling
 *      added SC_COMMANDNAME for convienence
 *      rename do_game_board to do_smaug_chess
 * 0.25:
 *      pawn promotion
 * 0.26:
 *      pp bug fixes
 * 0.27:
 *      king movement fixes
 * 0.28:
 *      fixed an instance for a king in checkmate could move
 * 0.29:
 *      ported imc support to IMC2 Antifreeze CL-1 (untested)
 * 0.30:
 *      fixes to IMC2 support
 * 0.31:
 *      Upgrade to IMC2 AntiFreeze CL-2 (this means you)
 */

#include <stdio.h>
#include <string.h>

#include "mud.h"
#define SC_VERSION "0.31"
#define SC_INFO "SMAUG Chess v" SC_VERSION ", by Jesse DeFer, jdefer@dotd.com"
#define SC_CVSID "$Id: chess.c,v 1.37 2004/04/01 02:55:49 dotd Exp $"
#define SC_COMMANDNAME "chess"

#ifndef GET_NAME
#define GET_NAME(ch) (ch)->name
#endif
#define CHESS_NAME(ch,board) (board->type==TYPE_IMC?ch:GET_NAME(ch))

/*#define USE_IMC*/	/* Uncomment for IMC */
/*#define STOCK_COLOR*/	/* Uncomment for non DOTDII codebases*/
/*#define send_to_char send_to_char_color*/ /* Uncomment if required */

#define WHITE_BACKGROUND	"^w"
#define BLACK_BACKGROUND	"^x"
#define WHITE_FOREGROUND	"&W"
#define BLACK_FOREGROUND	"&x"

#ifdef USE_IMC
#include "imcsdk.h"

void imc_send_chess(CHAR_DATA *ch, char *to, char *argument);
#endif

void do_smaug_chess(CHAR_DATA *ch, char *argument);

const char *big_pieces[MAX_PIECES][2] = {
    {
        "%s       ",
        "%s       "
    },
    {
        "%s  (-)  ",
        "%s  -|-  "
    },
    {
        "%s  ###  ",
        "%s  { }  "
    },
    {
        "%s  ###  ",
        "%s  { }  "
    },
    {
        "%s  /-@- ",
        "%s / /   "
    },
    {
        "%s  () + ",
        "%s  {}-| "
    },
    {
        "%s   @   ",
        "%s  /+\\  "
    },
    {
        "%s  ^^^^^^  ",
        "%s  {@}  "
    },
    {
        "%s  [-]  ",
        "%s  -|-  "
    },
    {
        "%s  ###  ",
        "%s  [ ]  "
    },
    {
        "%s  ###  ",
        "%s  [ ]  "
    },
    {
        "%s  /-*- ",
        "%s / /   "
    },
    {
        "%s  [] + ",
        "%s  {}-| "
    },
    {
        "%s   #   ",
        "%s  /+\\  "
    },
    {
        "%s  ^^^^^^  ",
        "%s  [#]  "
    }
};

const char small_pieces[MAX_PIECES+1] = " prrnbqkPRRNBQK";

const char *piece_names[MAX_PIECES] =
{
    "empty",
    "pawn", "rook", "rook", "knight", "bishop", "queen", "king",
    "pawn", "rook", "rook", "knight", "bishop", "queen", "king"
};

static char *print_big_board(CHAR_DATA *ch, GAME_BOARD_DATA *board)
{
    static char retbuf[MAX_STRING_LENGTH*2];
    char buf[MAX_STRING_LENGTH], buf2[MAX_STRING_LENGTH];
    char s1[16], s2[16];
    int x,y;

#ifdef STOCK_COLOR
    sprintf(s1,WHITE_FOREGROUND);
    sprintf(s2,BLACK_FOREGROUND);
#else
    sprintf(s1,"%s",atcode_color_str(ch->colors[AT_CHESS1]));
    sprintf(s2,"%s",atcode_color_str(ch->colors[AT_CHESS2]));
#endif

    sprintf(retbuf,WHITE_FOREGROUND "     1      2      3      4      5      6      7      8\n\r");

    for (x=0;x<8;x++)
    {
        strcat(retbuf,"  ");
        for (y=0;y<8;y++)
        {
            sprintf(buf,"%s%s",
                    x%2==0 ? (y%2==0 ? BLACK_BACKGROUND : WHITE_BACKGROUND) : \
                    (y%2==0 ? WHITE_BACKGROUND : BLACK_BACKGROUND),
                    big_pieces[board->board[x][y]][0]);
            sprintf(buf2,buf,IS_WHITE(board->board[x][y]) ? s1 : s2);
            strcat(retbuf,buf2);
        }
        strcat(retbuf, BLACK_BACKGROUND "\n\r");

        sprintf(buf, WHITE_FOREGROUND "%c ", 'A'+x);
        strcat(retbuf,buf);
        for (y=0;y<8;y++)
        {
            sprintf(buf,"%s%s",
                    x%2==0 ? (y%2==0 ? BLACK_BACKGROUND : WHITE_BACKGROUND) : \
                    (y%2==0 ? WHITE_BACKGROUND : BLACK_BACKGROUND),
                    big_pieces[board->board[x][y]][1]);
            sprintf(buf2,buf,IS_WHITE(board->board[x][y]) ? s1 : s2);
            strcat(retbuf,buf2);
        }
        strcat(retbuf, BLACK_BACKGROUND "\n\r");
    }

    return(retbuf);
}

static char *srow(CHAR_DATA *ch, GAME_BOARD_DATA *board, int x, int y)
{
    char buf[MAX_INPUT_LENGTH], s1[16], s2[16];

#ifdef STOCK_COLOR
    sprintf(s1,WHITE_FOREGROUND);
    sprintf(s2,BLACK_FOREGROUND);
#else
    sprintf(s1,"%s",atcode_color_str(ch->colors[AT_CHESS1]));
    sprintf(s2,"%s",atcode_color_str(ch->colors[AT_CHESS2]));
#endif

    sprintf(buf,"%s%s%c",
            x%2==0 ? (y%2==0 ? BLACK_BACKGROUND : WHITE_BACKGROUND) : \
            (y%2==0 ? WHITE_BACKGROUND : BLACK_BACKGROUND),
            board->board[x][y] == NO_PIECE ? "" : \
            ( IS_WHITE(board->board[x][y]) ? s1 : s2 ),
            small_pieces[board->board[x][y]]);

    return(str_dup(buf));
}

static char *print_small_board(CHAR_DATA *ch, GAME_BOARD_DATA *board)
{
    static char buf[MAX_STRING_LENGTH];
    char buf2[MAX_STRING_LENGTH];
    char *a,*b,*c,*d,*e,*f,*g,*h;
    int x;

    sprintf(buf,BLACK_BACKGROUND WHITE_FOREGROUND \
            "   12345678\n\r  +--------+\n\r");

    for (x=0;x<8;x++)
    {
        a=srow(ch,board,x,0); b=srow(ch,board,x,1);
        c=srow(ch,board,x,2); d=srow(ch,board,x,3);
        e=srow(ch,board,x,4); f=srow(ch,board,x,5);
        g=srow(ch,board,x,6); h=srow(ch,board,x,7);
        sprintf(buf2,
                BLACK_BACKGROUND WHITE_FOREGROUND "%c |%s%s%s%s%s%s%s%s" \
                BLACK_BACKGROUND WHITE_FOREGROUND "| %c\n\r",
                'A'+x, a, b, c, d, e, f, g, h, 'A'+x );
        free(a); free(b); free(c); free(d);
        free(e); free(f); free(g); free(h);
        strcat(buf,buf2);
    }

    sprintf(buf2, BLACK_BACKGROUND WHITE_FOREGROUND \
            "  +--------+\n\r   12345678\n\r");
    strcat(buf,buf2);

    return(buf);
}

static void init_board(GAME_BOARD_DATA *board, bool imc)
{
    int x,y;
    for (x=0;x<8;x++)
        for (y=0;y<8;y++)
            board->board[x][y] = 0;
    board->board[0][0] = WHITE_QROOK;
    board->board[0][1] = WHITE_KNIGHT;
    board->board[0][2] = WHITE_BISHOP;
    if (!imc)
    {
	board->board[0][3] = WHITE_QUEEN;
	board->board[0][4] = WHITE_KING;
    }
    else
    {
	board->board[0][4] = WHITE_QUEEN;
	board->board[0][3] = WHITE_KING;
    }
    board->board[0][5] = WHITE_BISHOP;
    board->board[0][6] = WHITE_KNIGHT;
    board->board[0][7] = WHITE_KROOK;
    for (x=0;x<8;x++)
        board->board[1][x] = WHITE_PAWN;
    for (x=0;x<8;x++)
        board->board[6][x] = BLACK_PAWN;
    board->board[7][0] = BLACK_QROOK;
    board->board[7][1] = BLACK_KNIGHT;
    board->board[7][2] = BLACK_BISHOP;
    if (!imc)
    {
	board->board[7][3] = BLACK_QUEEN;
	board->board[7][4] = BLACK_KING;
    }
    else
    {
	board->board[7][4] = BLACK_QUEEN;
	board->board[7][3] = BLACK_KING;
    }
    board->board[7][5] = BLACK_BISHOP;
    board->board[7][6] = BLACK_KNIGHT;
    board->board[7][7] = BLACK_KROOK;
    for (x=0;x<MAX_PIECES;x++)
        board->moved[x] = 0;
    board->player1 = NULL;
    board->player2 = NULL;
    board->turn = NULL;
    board->moves = 0;
    board->type = TYPE_LOCAL;
}

static bool find_piece(GAME_BOARD_DATA *board, int *x, int *y, int piece)
{
    int a,b;

    for (a=0;a<8;a++)
    {
        for (b=0;b<8;b++)
            if (board->board[a][b] == piece)
                break;
        if (board->board[a][b] == piece)
            break;
    }
    *x = a;
    *y = b;
    if (board->board[a][b] == piece)
        return TRUE;
    return FALSE;
}

#define SAME_COLOR(x1,y1,x2,y2)	\
    ((IS_WHITE(board->board[x1][y1]) && IS_WHITE(board->board[x2][y2])) || \
    (IS_BLACK(board->board[x1][y1]) && IS_BLACK(board->board[x2][y2])))

static bool king_in_check(GAME_BOARD_DATA *board, int piece)
{
    int x=0,y=0,l,m;

    if ( piece != WHITE_KING && piece != BLACK_KING )
        return FALSE;

    if (!find_piece(board,&x,&y,piece))
        return FALSE;

    if ( x<0 || y<0 || x>7 || y>7 )
        return FALSE;

    /* pawns */
    if ( IS_WHITE(piece) && x < 7 &&
         (( y > 0 && IS_BLACK(board->board[x+1][y-1]) ) ||
          ( y < 7 && IS_BLACK(board->board[x+1][y+1]) )))
        return TRUE;
    else if ( IS_BLACK(piece) && x > 0 &&
              (( y > 0 && IS_WHITE(board->board[x-1][y-1]) ) ||
               ( y < 7 && IS_WHITE(board->board[x-1][y+1]) )))
        return TRUE;
    /* knights */
    if ( x-2 >= 0 && y-1 >= 0 &&
         ( (board->board[x-2][y-1] == BLACK_KNIGHT && IS_WHITE(board->board[x][y])) ||
           (board->board[x-2][y-1] == WHITE_KNIGHT && IS_BLACK(board->board[x][y])) ))
        return TRUE;
    if ( x-2 >= 0 && y+1 < 8 &&
         ( (board->board[x-2][y+1] == BLACK_KNIGHT && IS_WHITE(board->board[x][y])) ||
           (board->board[x-2][y+1] == WHITE_KNIGHT && IS_BLACK(board->board[x][y])) ))
        return TRUE;

    if ( x-1 >= 0 && y-2 >= 0 &&
         ( (board->board[x-1][y-2] == BLACK_KNIGHT && IS_WHITE(board->board[x][y])) ||
           (board->board[x-1][y-2] == WHITE_KNIGHT && IS_BLACK(board->board[x][y])) ))
        return TRUE;
    if ( x-1 >= 0 && y+2 < 8 &&
         ( (board->board[x-1][y+2] == BLACK_KNIGHT && IS_WHITE(board->board[x][y])) ||
           (board->board[x-1][y+2] == WHITE_KNIGHT && IS_BLACK(board->board[x][y])) ))
        return TRUE;

    if ( x+1 < 8 && y-2 >= 0 &&
         ( (board->board[x+1][y-2] == BLACK_KNIGHT && IS_WHITE(board->board[x][y])) ||
           (board->board[x+1][y-2] == WHITE_KNIGHT && IS_BLACK(board->board[x][y])) ))
        return TRUE;
    if ( x+1 < 8 && y+2 < 8 &&
         ( (board->board[x+1][y+2] == BLACK_KNIGHT && IS_WHITE(board->board[x][y])) ||
           (board->board[x+1][y+2] == WHITE_KNIGHT && IS_BLACK(board->board[x][y])) ))
        return TRUE;

    if ( x+2 < 8 && y-1 >= 0 &&
         ( (board->board[x+2][y-1] == BLACK_KNIGHT && IS_WHITE(board->board[x][y])) ||
           (board->board[x+2][y-1] == WHITE_KNIGHT && IS_BLACK(board->board[x][y])) ))
        return TRUE;
    if ( x+2 < 8 && y+1 < 8 &&
         ( (board->board[x+2][y+1] == BLACK_KNIGHT && IS_WHITE(board->board[x][y])) ||
           (board->board[x+2][y+1] == WHITE_KNIGHT && IS_BLACK(board->board[x][y])) ))
        return TRUE;

    /* horizontal/vertical long distance */
    for (l=x+1;l<8;l++)
        if ( board->board[l][y] != NO_PIECE )
        {
            if ( SAME_COLOR(x,y,l,y) )
                break;
            if ( board->board[l][y] == BLACK_QUEEN || board->board[l][y] == WHITE_QUEEN ||
                 board->board[l][y] == BLACK_QROOK || board->board[l][y] == WHITE_QROOK ||
                 board->board[l][y] == BLACK_KROOK || board->board[l][y] == WHITE_KROOK )
                return TRUE;
            break;
        }
    for (l=x-1;l>=0;l--)
        if ( board->board[l][y] != NO_PIECE )
        {
            if ( SAME_COLOR(x,y,l,y) )
                break;
            if ( board->board[l][y] == BLACK_QUEEN || board->board[l][y] == WHITE_QUEEN ||
                 board->board[l][y] == BLACK_QROOK || board->board[l][y] == WHITE_QROOK ||
                 board->board[l][y] == BLACK_KROOK || board->board[l][y] == WHITE_KROOK )
                return TRUE;
            break;
        }
    for (m=y+1;m<8;m++)
        if ( board->board[x][m] != NO_PIECE )
        {
            if ( SAME_COLOR(x,y,x,m) )
                break;
            if ( board->board[x][m] == BLACK_QUEEN || board->board[x][m] == WHITE_QUEEN ||
                 board->board[x][m] == BLACK_QROOK || board->board[x][m] == WHITE_QROOK ||
                 board->board[x][m] == BLACK_KROOK || board->board[x][m] == WHITE_KROOK )
                return TRUE;
            break;
        }
    for (m=y-1;m>=0;m--)
        if ( board->board[x][m] != NO_PIECE )
        {
            if ( SAME_COLOR(x,y,x,m) )
                break;
            if ( board->board[x][m] == BLACK_QUEEN || board->board[x][m] == WHITE_QUEEN ||
                 board->board[x][m] == BLACK_QROOK || board->board[x][m] == WHITE_QROOK ||
                 board->board[x][m] == BLACK_KROOK || board->board[x][m] == WHITE_KROOK )
                return TRUE;
            break;
        }
    /* diagonal long distance */
    for (l=x+1,m=y+1;l<8 && m<8;l++,m++)
        if ( board->board[l][m] != NO_PIECE )
        {
            if ( SAME_COLOR(x,y,l,m) )
                break;
            if ( board->board[l][m] == BLACK_QUEEN || board->board[l][m] == WHITE_QUEEN ||
                 board->board[l][m] == BLACK_BISHOP || board->board[l][m] == WHITE_BISHOP )
                return TRUE;
            break;
        }
    for (l=x-1,m=y+1;l>=0 && m<8;l--,m++)
        if ( board->board[l][m] != NO_PIECE )
        {
            if ( SAME_COLOR(x,y,l,m) )
                break;
            if ( board->board[l][m] == BLACK_QUEEN || board->board[l][m] == WHITE_QUEEN ||
                 board->board[l][m] == BLACK_BISHOP || board->board[l][m] == WHITE_BISHOP )
                return TRUE;
            break;
        }
    for (l=x+1,m=y-1;l<8 && m>=0;l++,m--)
        if ( board->board[l][m] != NO_PIECE )
        {
            if ( SAME_COLOR(x,y,l,m) )
                break;
            if ( board->board[l][m] == BLACK_QUEEN || board->board[l][m] == WHITE_QUEEN ||
                 board->board[l][m] == BLACK_BISHOP || board->board[l][m] == WHITE_BISHOP )
                return TRUE;
            break;
        }
    for (l=x-1,m=y-1;l>=0 && m>=0;l--,m--)
        if ( board->board[l][m] != NO_PIECE )
        {
            if ( SAME_COLOR(x,y,l,m) )
                break;
            if ( board->board[l][m] == BLACK_QUEEN || board->board[l][m] == WHITE_QUEEN ||
                 board->board[l][m] == BLACK_BISHOP || board->board[l][m] == WHITE_BISHOP )
                return TRUE;
            break;
        }
    return FALSE;
}

static bool king_in_possible_checkmate(GAME_BOARD_DATA *board, int piece)
{
    int x=0,y=0,dx,dy,sk=0;

    if ( piece != WHITE_KING && piece != BLACK_KING )
        return FALSE;

    if (!find_piece(board,&x,&y,piece))
        return FALSE;

    if ( x<0 || y<0 || x>7 || y>7 )
        return FALSE;

    if (!king_in_check(board,board->board[x][y]))
        return FALSE;

    dx = x+1;
    dy = y+1;
    if ( dx < 8 && dy < 8 && board->board[dx][dy] == NO_PIECE )
    {
        sk = board->board[dx][dy] = board->board[x][y];
        board->board[x][y] = NO_PIECE;
        if (!king_in_check(board,sk))
        {
            board->board[x][y] = sk;
            board->board[dx][dy] = NO_PIECE;
            return FALSE;
        }
        board->board[x][y] = sk;
        board->board[dx][dy] = NO_PIECE;
    }
    dx = x-1;
    dy = y+1;
    if ( dx >= 0 && dy < 8 && board->board[dx][dy] == NO_PIECE )
    {
        sk = board->board[dx][dy] = board->board[x][y];
        board->board[x][y] = NO_PIECE;
        if (!king_in_check(board,sk))
        {
            board->board[x][y] = sk;
            board->board[dx][dy] = NO_PIECE;
            return FALSE;
        }
        board->board[x][y] = sk;
        board->board[dx][dy] = NO_PIECE;
    }
    dx = x+1;
    dy = y-1;
    if ( dx < 8 && dy >= 0 && board->board[dx][dy] == NO_PIECE )
    {
        sk = board->board[dx][dy] = board->board[x][y];
        board->board[x][y] = NO_PIECE;
        if (!king_in_check(board,sk))
        {
            board->board[x][y] = sk;
            board->board[dx][dy] = NO_PIECE;
            return FALSE;
        }
        board->board[x][y] = sk;
        board->board[dx][dy] = NO_PIECE;
    }
    dx = x-1;
    dy = y-1;
    if ( dx >= 0 && dy >= 0 && board->board[dx][dy] == NO_PIECE )
    {
        sk = board->board[dx][dy] = board->board[x][y];
        board->board[x][y] = NO_PIECE;
        if (!king_in_check(board,sk))
        {
            board->board[x][y] = sk;
            board->board[dx][dy] = NO_PIECE;
            return FALSE;
        }
        board->board[x][y] = sk;
        board->board[dx][dy] = NO_PIECE;
    }
    dx = x;
    dy = y+1;
    if ( dy < 8 && board->board[dx][dy] == NO_PIECE )
    {
        sk = board->board[dx][dy] = board->board[x][y];
        board->board[x][y] = NO_PIECE;
        if (!king_in_check(board,sk))
        {
            board->board[x][y] = sk;
            board->board[dx][dy] = NO_PIECE;
            return FALSE;
        }
        board->board[x][y] = sk;
        board->board[dx][dy] = NO_PIECE;
    }
    dx = x;
    dy = y-1;
    if ( dy >= 0 && board->board[dx][dy] == NO_PIECE )
    {
        sk = board->board[dx][dy] = board->board[x][y];
        board->board[x][y] = NO_PIECE;
        if (!king_in_check(board,sk))
        {
            board->board[x][y] = sk;
            board->board[dx][dy] = NO_PIECE;
            return FALSE;
        }
        board->board[x][y] = sk;
        board->board[dx][dy] = NO_PIECE;
    }
    dx = x+1;
    dy = y;
    if ( dx < 8 && board->board[dx][dy] == NO_PIECE )
    {
        sk = board->board[dx][dy] = board->board[x][y];
        board->board[x][y] = NO_PIECE;
        if (!king_in_check(board,sk))
        {
            board->board[x][y] = sk;
            board->board[dx][dy] = NO_PIECE;
            return FALSE;
        }
        board->board[x][y] = sk;
        board->board[dx][dy] = NO_PIECE;
    }
    dx = x-1;
    dy = y;
    if ( dx >= 0 && board->board[dx][dy] == NO_PIECE )
    {
        sk = board->board[dx][dy] = board->board[x][y];
        board->board[x][y] = NO_PIECE;
        if (!king_in_check(board,sk))
        {
            board->board[x][y] = sk;
            board->board[dx][dy] = NO_PIECE;
            return FALSE;
        }
        board->board[x][y] = sk;
        board->board[dx][dy] = NO_PIECE;
    }
    return TRUE;
}

static int is_valid_move(CHAR_DATA *ch, GAME_BOARD_DATA *board, int x, int y, int dx, int dy)
{
    if ( dx<0 || dy<0 || dx>7 || dy>7 )
        return MOVE_OFFBOARD;

    if ( board->board[x][y] == NO_PIECE )
        return MOVE_INVALID;

    if ( x == dx && y == dy )
        return MOVE_INVALID;

    if ( IS_WHITE(board->board[x][y]) && board->player1 == ch )
        return MOVE_WRONGCOLOR;
    if ( IS_BLACK(board->board[x][y]) && (board->player2 == ch || !ch) )
        return MOVE_WRONGCOLOR;

    switch (board->board[x][y])
    {
    case WHITE_PAWN:    case BLACK_PAWN:
        if ( IS_WHITE(board->board[x][y]) &&
             dx == x+2 && x == 1 && dy == y &&
             board->board[dx][dy] == NO_PIECE &&
             board->board[x+1][dy] == NO_PIECE )
            return MOVE_OK;
        else if ( IS_BLACK(board->board[x][y]) &&
                  dx == x-2 && x == 6 && dy == y &&
                  board->board[dx][dy] == NO_PIECE &&
                  board->board[x-1][dy] == NO_PIECE )
            return MOVE_OK;
        if ( IS_WHITE(board->board[x][y]) && dx != x+1 )
            return MOVE_INVALID;
        else if ( IS_BLACK(board->board[x][y]) && dx != x-1 )
            return MOVE_INVALID;
        if ( dy != y && dy != y-1 && dy != y+1 )
            return MOVE_INVALID;
        if ( dy == y )
        {
            if ( board->board[dx][dy] == NO_PIECE)
                return MOVE_OK;
            else if ( SAME_COLOR(x,y,dx,dy) )
                return MOVE_SAMECOLOR;
            else
                return MOVE_BLOCKED;
        }
        else
        {
            if ( board->board[dx][dy] == NO_PIECE )
                return MOVE_INVALID;
            else if ( SAME_COLOR(x,y,dx,dy) )
                return MOVE_SAMECOLOR;
            else if ( board->board[dx][dy] != BLACK_KING &&
                      board->board[dx][dy] != WHITE_KING )
                return MOVE_TAKEN;
            else
                return MOVE_INVALID;
        }
        break;
    case WHITE_QROOK:	case BLACK_QROOK:
    case WHITE_KROOK:	case BLACK_KROOK:
        {
            int cnt;

            if ( dx != x && dy != y )
                return MOVE_INVALID;

            if ( dx == x)
            {
                for (cnt = y; cnt != dy; )
                {
                    if ( cnt != y && board->board[x][cnt] != NO_PIECE )
                        return MOVE_BLOCKED;
                    if ( dy > y )
                        cnt++;
                    else
                        cnt--;
                }
            }
            else if ( dy == y)
            {
                for (cnt = x; cnt != dx; )
                {
                    if ( cnt !=x && board->board[cnt][y] != NO_PIECE )
                        return MOVE_BLOCKED;
                    if ( dx > x )
                        cnt++;
                    else
                        cnt--;
                }
            }

            if ( board->board[dx][dy] == NO_PIECE )
                return MOVE_OK;

            if ( !SAME_COLOR(x,y,dx,dy) )
                return MOVE_TAKEN;

            return MOVE_SAMECOLOR;
        }
        break;
    case WHITE_KNIGHT:	case BLACK_KNIGHT:
        if ( (dx == x-2 && dy == y-1) ||
             (dx == x-2 && dy == y+1) ||
             (dx == x-1 && dy == y-2) ||
             (dx == x-1 && dy == y+2) ||
             (dx == x+1 && dy == y-2) ||
             (dx == x+1 && dy == y+2) ||
             (dx == x+2 && dy == y-1) ||
             (dx == x+2 && dy == y+1) )
        {
            if ( board->board[dx][dy] == NO_PIECE )
                return MOVE_OK;
            if ( SAME_COLOR(x,y,dx,dy) )
                return MOVE_SAMECOLOR;
            return MOVE_TAKEN;
        }
        return MOVE_INVALID;
        break;
    case WHITE_BISHOP:	case BLACK_BISHOP:
        {
            int l, m, blocked = FALSE;

            if ( dx == x || dy == y )
                return MOVE_INVALID;

            l = x;
            m = y;

            while ( 1 )
            {
                if ( dx > x )
                    l++;
                else
                    l--;
                if ( dy > y )
                    m++;
                else
                    m--;
                if ( l > 7 || m > 7 || l < 0 || m < 0 )
                    return MOVE_INVALID;
                if ( l == dx && m == dy )
                    break;
                if ( board->board[l][m] != NO_PIECE )
                    blocked = TRUE;
            }
            if ( l != dx || m != dy )
                return MOVE_INVALID;

            if ( blocked )
                return MOVE_BLOCKED;

            if ( board->board[dx][dy] == NO_PIECE )
                return MOVE_OK;

            if ( !SAME_COLOR(x,y,dx,dy) )
                return MOVE_TAKEN;

            return MOVE_SAMECOLOR;
        }
        break;
    case WHITE_QUEEN:	case BLACK_QUEEN:
        {
            int l, m, blocked = FALSE;

            l = x;
            m = y;

            while ( 1 )
            {
                if ( dx > x )
                    l++;
                else if ( dx < x )
                    l--;
                if ( dy > y )
                    m++;
                else if ( dy < y )
                    m--;
                if ( l > 7 || m > 7 || l < 0 || m < 0 )
                    return MOVE_INVALID;
                if ( l == dx && m == dy )
                    break;
                if ( board->board[l][m] != NO_PIECE )
                    blocked = TRUE;
            }
            if ( l != dx || m != dy )
                return MOVE_INVALID;

            if ( blocked )
                return MOVE_BLOCKED;

            if ( board->board[dx][dy] == NO_PIECE )
                return MOVE_OK;

            if ( !SAME_COLOR(x,y,dx,dy) )
                return MOVE_TAKEN;

            return MOVE_SAMECOLOR;
        }
        break;
    case WHITE_KING:	case BLACK_KING:
        {
            int sp,sk;

            if ( dx > x+1 || dx < x-1 || dy > y+1 || dy < y-1 )
                return MOVE_INVALID;

            if ( SAME_COLOR(x,y,dx,dy) )
                return MOVE_SAMECOLOR;

            sk = board->board[x][y];
            sp = board->board[dx][dy];
            board->board[x][y] = NO_PIECE;
            board->board[dx][dy] = sk;

            if (king_in_check(board,sk))
            {
                board->board[x][y] = sk;
                board->board[dx][dy] = sp;
                return MOVE_CHECK;
            }

            board->board[x][y] = sk;
            board->board[dx][dy] = sp;

            if ( board->board[dx][dy] == NO_PIECE )
                return MOVE_OK;

            return MOVE_TAKEN;
        }
        break;
    default:
        bug("is_valid_move():chess.c invaild piece: %d", board->board[x][y]);
        return MOVE_INVALID;
        break;
    }

    bug("is_valid_move():chess.c shouldn't get here");
    return MOVE_OK;
}

#undef SAME_COLOR

static void board_move_stuff( GAME_BOARD_DATA *board )
{
    board->moves++;
    if (board->turn == board->player1)
        board->turn = board->player2;
    else
        board->turn = board->player1;
}

void free_game( GAME_BOARD_DATA *board )
{
    if ( !board )
        return;
#ifdef USE_IMC
    if ( board->type == TYPE_IMC )
    {
        imc_send_chess((CHAR_DATA *)board->player1?:NULL, (char *)board->player2, "stop");
        if (board->player2)
            DISPOSE(board->player2);
    }
#endif
    if ( board->player1 )
    {
        CHAR_DATA *ch = (CHAR_DATA *)board->player1;
        ch_printf(ch, "The game has been stopped at %d total moves.\n\r", board->moves);
        ch->pcdata->game_board = NULL;
    }
    if ( board->player2 )
    {
        CHAR_DATA *ch = (CHAR_DATA *)board->player2;
        ch_printf(ch, "The game has been stopped at %d total moves.\n\r", board->moves);
        ch->pcdata->game_board = NULL;
    }
    board->player1 = NULL;
    board->player2 = NULL;
    DISPOSE(board);
}

#ifdef USE_IMC
void imc_send_chess(CHAR_DATA *ch, char *to, char *argument)
{
    PACKET out;

    if ( !ch || imc_active<IA_UP )
        return;

    if( !strcasecmp( imc_mudof( to ), "*" ) )
	return; /* don't let them do this */

    setdata(&out, imc_getdata(ch));

    imcstrlcpy( out.to, to, IMC_NAME_LENGTH );
    imcstrlcpy( out.type, "chess", IMC_TYPE_LENGTH );
    imc_addkey( &out, "text", argument );

    imc_send(&out);
    imc_freedata(&out);
}

void imc_recv_chess(imc_char_data *from, PACKET *p)
{
    DESCRIPTOR_DATA *d;
    CHAR_DATA *victim, *vch;
    char buf[MAX_INPUT_LENGTH];
    char *argument;

    argument = imc_getkey( p, "text", "" );

    if ( !strcmp(p->to, "*") )
        return;

    victim=NULL;
    for ( d=first_descriptor; d; d=d->next )
    {
        if ( d->connected==CON_PLAYING &&
             (vch=d->original ? d->original : d->character)!=NULL &&
             !IS_NPC(vch) )
        {
            if ( !str_cmp(p->to, GET_NAME(vch)) )
            {
                victim=vch;
                break;
            }
            if ( is_name(p->to, GET_NAME(vch)) )
                victim=vch;
        }
    }

    if ( !victim )
    {
        if ( !str_cmp(argument, "stop") )
            return;
        sprintf(buf, "%s is not here.", p->to);
        imc_send_tell(NULL, from->name, buf, 1);
        return;
    }

    if ( !victim->pcdata->game_board )
    {
        if ( !str_cmp(argument, "stop") )
            return;
        sprintf(buf, "%s is not ready to be joined in a game.", p->to);
        imc_send_tell(NULL, from->name, buf, 1);
        imc_send_chess((CHAR_DATA *)victim->pcdata->game_board->player1?:NULL,(char *)from->name,"stop");
        return;
    }

    if ( !str_cmp(argument, "start") )
    {
        if ( victim->pcdata->game_board->player2 != NULL )
        {
            sprintf(buf, "%s is already playing a game.", p->to);
            imc_send_tell(NULL, from->name, buf, 1);
            imc_send_chess((CHAR_DATA *)victim->pcdata->game_board->player1?:NULL,(char *)from->name,"stop");
            return;
        }
        victim->pcdata->game_board->player2 = str_dup(from->name);
        victim->pcdata->game_board->moves = 0;
        victim->pcdata->game_board->type = TYPE_IMC;
        ch_printf(victim, "%s has joined your game.\n\r", from->name);
        imc_send_chess(victim, from->name, "accepted");
        return;
    }
    if ( !str_cmp(argument, "accepted") )
    {
        if ( !victim->pcdata->game_board ||
             victim->pcdata->game_board->player2 == NULL ||
             victim->pcdata->game_board->type != TYPE_IMC ||
             str_cmp((char *)victim->pcdata->game_board->player2,from->name) )
        {
            imc_send_chess((CHAR_DATA *)victim->pcdata->game_board->player1?:NULL,(char *)from->name, "stop");
            return;
        }
        ch_printf(victim,"You have joined %s in a game.\n\r", from->name);
        if (victim->pcdata->game_board->player2)
            DISPOSE(victim->pcdata->game_board->player2);
        victim->pcdata->game_board->player2 = str_dup(from->name);
        victim->pcdata->game_board->moves = 0;
        return;
    }
    if ( !str_cmp(argument, "stop") )
    {
        ch_printf(victim, "%s has stopped the game.\n\r", from->name);
        free_game(victim->pcdata->game_board);
        return;
    }
    if ( !str_cmp(argument, "forfeit") )
    {
        ch_printf(victim, "%s has forfeited the game, you win!\n\r", from->name);
        free_game(victim->pcdata->game_board);
        return;
    }
    if ( !str_cmp(argument, "invalidmove") )
    {
        send_to_char("You have issued an invalid move according to the other mud.\n\r", victim);
        do_smaug_chess(victim, "stop");
        return;
    }
    if ( !str_cmp(argument, "moveok") )
    {
        send_to_char("The other mud has accepted your move.\n\r", victim);
        return;
    }

    if ( !str_prefix("move", argument) )
    {
        char a,b;
        int x,y,dx,dy,ret;
        a=b=' ';
        x=y=dx=dy=-1;
        if (sscanf(argument, "move %c%d %c%d",&a,&y,&b,&dy) != 4 ||
            a<'0' || a>'7' || b<'0' || b>'7' || y<0 || y>7 || dy<0 || dy>7)
        {
            imc_send_chess((CHAR_DATA *)victim->pcdata->game_board->player1?:NULL,(char *)from->name, "invalidmove");
            return;
        }
        x = a - '0';
        dx = b - '0';
        x = (7-x);
        y = (7-y);
        dx = (7-dx);
        dy = (7-dy);
        log_printf_plus(LOG_DEBUG, LEVEL_LOG_CSET, SEV_SPAM, "IMC Chess move: %d, %d -> %d, %d", x,y,dx,dy);
        ret = is_valid_move(NULL,victim->pcdata->game_board,x,y,dx,dy);
        if (ret == MOVE_OK || ret == MOVE_TAKEN)
        {
            GAME_BOARD_DATA *board;
            int piece, destpiece;
            board = victim->pcdata->game_board;
            piece = board->board[x][y];
            destpiece = board->board[dx][dy];
            board->board[dx][dy] = piece;
            board->board[x][y] = NO_PIECE;
            if ( king_in_check(board,IS_WHITE(board->board[dx][dy])?WHITE_KING:BLACK_KING) &&
                 ( board->board[dx][dy]!=WHITE_KING && board->board[dx][dy]!=BLACK_KING ))
            {
                board->board[dx][dy] = destpiece;
		board->board[x][y] = piece;
                /* fall through and send invalidmove */
            }
            else
	    {
		ch_printf(victim, "%s has moved, %c%d (%s) to %c%d (%s)\n\r",
			  from->name,
			  x + 'a', y+1, piece_names[piece],
			  dx + 'a', dy+1, piece_names[destpiece]);

                board_move_stuff(board);
                board->lastx = dx;
                board->lasty = dy;
                board->moved[piece]++;
                board->moved[destpiece]++; /* taken pieces marked as moved */
                imc_send_chess((CHAR_DATA *)board->player1?:NULL,(char *)from->name, "moveok");
                return;
            }
	}
/*	ch_printf(victim, "%s has tried to move, %c%d (%s) to %c%d (%s)\n\r",
		  from->name,
		  x + 'a', y+1, piece_names[victim->pcdata->game_board->board[x][y]],
		  dx + 'a', dy+1, piece_names[victim->pcdata->game_board->board[dx][dy]]);*/
        ch_printf(victim, "%s has made an invalid move, aborting the game.\n\r", from->name);
	log_printf_plus(LOG_IMC, LEVEL_LOG_CSET, SEV_ERR, "IMC Chess invalidmove: ret=%d", ret);
	imc_send_chess((CHAR_DATA *)victim->pcdata->game_board->player1?:NULL,(char *)from->name, "invalidmove");
        return;
    }

    sprintf(log_buf, "Unknown chess command from: %s, %s", from->name, argument);
    log_string_plus(log_buf, LOG_IMC, LEVEL_LOG_CSET, SEV_ERR);
}
#endif

static void board_move_messages(CHAR_DATA *ch, int ret, char *extra)
{
    CHAR_DATA *opp;
    char buf[MAX_INPUT_LENGTH];

    if ( ch == ch->pcdata->game_board->player1 )
        opp = (CHAR_DATA *)ch->pcdata->game_board->player2;
    else
        opp = (CHAR_DATA *)ch->pcdata->game_board->player1;
    /* don't send messages to imc opponent, their local system will display
       the messages to them itself */
#define SEND_TO_OPP(buf,opp) \
    if (opp) \
    { \
        if (ch->pcdata->game_board->type==TYPE_LOCAL) \
        { \
            send_to_char((buf),(opp)); \
            send_to_char("\n\r",(opp)); \
        } \
    }
    switch (ret)
    {
    case MOVE_OK:
        send_to_char("Ok.\n\r", ch);
        sprintf(buf, "%s has moved, %s.", GET_NAME(ch), extra);
        SEND_TO_OPP(buf, opp);
        break;
    case MOVE_CASTLE:
        send_to_char("Ok.\n\r", ch);
        sprintf(buf, "%s has castled %s.", GET_NAME(ch), extra);
        SEND_TO_OPP(buf, opp);
        break;
    case MOVE_PROMOTE:
        send_to_char("Ok.\n\r", ch);
        sprintf(buf, "%s has promoted a pawn to a %s.", GET_NAME(ch), extra);
        SEND_TO_OPP(buf, opp);
        break;
    case MOVE_INVALID:
        send_to_char("Invalid move.\n\r", ch);
        break;
    case MOVE_BLOCKED:
        send_to_char("You are blocked in that direction.\n\r", ch);
        break;
    case MOVE_TAKEN:
        send_to_char("You take the enemy's piece.\n\r", ch);
        sprintf(buf, "%s has moved, %s, and captured one of your pieces!", GET_NAME(ch), extra);
        SEND_TO_OPP(buf, opp);
        break;
    case MOVE_OFFBOARD:
        send_to_char("That move would be off the board.\n\r", ch);
        break;
    case MOVE_SAMECOLOR:
        send_to_char("Your own piece blocks the way.\n\r", ch);
        break;
    case MOVE_CHECK:
        send_to_char("That move would result in a check.\n\r", ch);
        sprintf(buf, "%s has attempted a move that would result in a check.", GET_NAME(ch));
        SEND_TO_OPP(buf, opp);
        break;
    case MOVE_WRONGCOLOR:
        send_to_char("That is not your piece.\n\r", ch);
        break;
    case MOVE_INCHECK:
        send_to_char("Your king would still be in check as a result of that move.\n\r", ch);
        break;
    default:
        bug("board_move_messages():chess.c unknown return value from is_valid_move():chess.c");
        break;
    }
#undef SEND_TO_OPP
}

void do_smaug_chess(CHAR_DATA *ch, char *argument)
{
    char arg[MAX_INPUT_LENGTH];

    argument = one_argument(argument, arg);

    if ( IS_NPC(ch) )
    {
        send_to_char("NPC's can't be in games.\n\r", ch);
        return;
    }

    if ( !str_cmp(arg, "_syntax") || !str_cmp(arg, "help") || arg[0] == '?' )
    {
        send_to_char(SC_INFO "\n\r", ch);
        if (IS_IMMORTAL(ch))
            send_to_char(SC_CVSID "\n\r", ch);
        send_to_char("Usage:    " SC_COMMANDNAME " <start|stop|join|forfeit>\n\r"
                     "Info:     " SC_COMMANDNAME " <status|board|sboard|pieces>\n\r"
                     "Movement: " SC_COMMANDNAME " <move> <source> <dest> [extra command]\n\r"
                     "Extras:   " SC_COMMANDNAME " <castle|promote>\n\r", ch);
        return;
    }

    if ( !str_cmp(arg, "start") )
    {
        GAME_BOARD_DATA *board;
        if ( ch->pcdata->game_board )
        {
            send_to_char("You are already in a game.\n\r", ch);
            return;
        }
        CREATE(board, GAME_BOARD_DATA, 1);
        init_board(board, FALSE);
        ch->pcdata->game_board = board;
        ch->pcdata->game_board->player1 = ch;
        ch->pcdata->game_board->turn = ch;
        send_to_char("You have started a game.\n\r", ch);
        return;
    }

    if ( !str_cmp(arg, "join") )
    {
        GAME_BOARD_DATA *board=NULL;
        CHAR_DATA *vch;
        char arg2[MAX_INPUT_LENGTH];
        if ( ch->pcdata->game_board )
        {
            send_to_char("You are already in a game.\n\r", ch);
            return;
        }
        argument = one_argument(argument,arg2);
        if ( arg[0] == '\0' )
        {
            send_to_char("Join who?\n\r", ch);
            return;
        }
#ifdef USE_IMC
        if ( strstr( arg2, "@" ) )
        {
            if (!str_cmp(imc_mudof(arg2), imc_siteinfo.localname))
            {
                send_to_char("That is this mud, don't use @.\n\r",ch);
                return;
            }
            send_to_char("Attempting to initiate IMC game...\n\r", ch);
            if (!str_cmp(imc_mudof(arg2), "*"))
            {
                send_to_char("* is not a valid mud name.\n\r",ch);
                return;
            }
            CREATE(board, GAME_BOARD_DATA, 1);
            init_board(board, TRUE);
            board->type = TYPE_IMC;
            board->player1 = (void *)ch;
            board->player2 = (void *)str_dup(arg2);
            board->turn = board->player2;
            board->moves = -1;
            ch->pcdata->game_board = board;
            imc_send_chess(ch,arg2,"start");
            return;
        }
#endif
        if ( !( vch = get_char_world(ch,arg2) ) )
        {
            send_to_char("Cannot find that player.\n\r", ch);
            return;
        }
        if ( IS_NPC(vch) )
        {
            send_to_char("That player is an NPC, and cannot play games.\n\r", ch);
            return;
        }
        board = vch->pcdata->game_board;
        if ( !board )
        {
            send_to_char("That player is not playing a game.\n\r", ch);
            return;
        }
        if ( board->player2 )
        {
            send_to_char("That game already has two players.\n\r", ch);
            return;
        }
        board->player2 = (void *)ch;
        board->turn = board->player2;
        ch->pcdata->game_board = board;
        send_to_char("You have joined a game, it is your turn.\n\r", ch);
        ch_printf((CHAR_DATA *)board->player1, "%s has joined your game.\n\r", GET_NAME(ch));
        return;
    }

    if ( !str_cmp(arg, "pieces") )
    {
        char buf[MAX_INPUT_LENGTH];
        int x;

        send_to_char("White pieces:\n\r", ch);
        for (x=WHITE_PAWN;x<=WHITE_KING;x++)
        {
            if (x == WHITE_KROOK)
                continue;
            ch_printf(ch, "%-7s", piece_names[x]);
        }
        send_to_char("\n\r", ch);
        for (x=WHITE_PAWN;x<=WHITE_KING;x++)
        {
            if (x == WHITE_KROOK)
                continue;
            sprintf(buf, "%s", big_pieces[x][0]);
            ch_printf(ch, buf, "");
        }
        send_to_char("\n\r", ch);
        for (x=WHITE_PAWN;x<=WHITE_KING;x++)
        {
            if (x == WHITE_KROOK)
                continue;
            sprintf(buf, "%s", big_pieces[x][1]);
            ch_printf(ch, buf, "");
        }
        send_to_char("\n\r\n\rBlack pieces:\n\r", ch);

        for (x=BLACK_PAWN;x<=BLACK_KING;x++)
        {
            if (x == BLACK_KROOK)
                continue;
            ch_printf(ch, "%-7s", piece_names[x]);
        }
        send_to_char("\n\r", ch);
        for (x=BLACK_PAWN;x<=BLACK_KING;x++)
        {
            if (x == BLACK_KROOK)
                continue;
            sprintf(buf, "%s", big_pieces[x][0]);
            ch_printf(ch, buf, "");
        }
        send_to_char("\n\r", ch);
        for (x=BLACK_PAWN;x<=BLACK_KING;x++)
        {
            if (x == BLACK_KROOK)
                continue;
            sprintf(buf, "%s", big_pieces[x][1]);
            ch_printf(ch, buf, "");
        }
        send_to_char("\n\r", ch);
 
        return;
    }

    if ( !ch->pcdata->game_board )
    {
        do_smaug_chess(ch, "_syntax");
        return;
    }

    if ( !str_cmp(arg, "stop") )
    {
        free_game(ch->pcdata->game_board);
        return;
    }

    if ( !str_cmp(arg, "forfeit") )
    {
        send_to_char("You forfeit the game, you loose!\n\r", ch);
        free_game(ch->pcdata->game_board);
        return;
    }

    if ( !str_cmp(arg, "status") )
    {
        GAME_BOARD_DATA *board = ch->pcdata->game_board;
        if ( !board->player1 )
            send_to_char("There is no black player.\n\r", ch);
        else if ( board->player1 == ch )
            send_to_char("You are black.\n\r", ch);
        else
            ch_printf(ch, "%s is black.\n\r",
                      GET_NAME((CHAR_DATA *)board->player1));
        if (king_in_possible_checkmate(board,BLACK_KING))
            send_to_char("The black king is possibly checkmated.\n\r", ch);
        else if (king_in_check(board,BLACK_KING))
            send_to_char("The black king is in check.\n\r", ch);
        if ( !board->player2 )
            send_to_char("There is no white player.\n\r", ch);
        else if ( board->player2 == ch )
            send_to_char("You are white.\n\r", ch);
        else
            ch_printf(ch, "%s is white.\n\r",
                      board->type == TYPE_LOCAL ?
                      GET_NAME((CHAR_DATA *)board->player2) :
                      (char *)board->player2);
        if (king_in_possible_checkmate(board,WHITE_KING))
            send_to_char("The white king is possibly checkmated.\n\r", ch);
        else if (king_in_check(board,WHITE_KING))
            send_to_char("The white king is in check.\n\r", ch);
        if ( !board->player2 ||
             !board->player1 )
            return;
        if (board->moves<0)
            send_to_char("The game hasn't started yet.\n\r", ch);
        else
            ch_printf(ch, "%d turns.\n\r", board->moves);
        if ( board->turn == ch )
            send_to_char("It is your turn.\n\r", ch);
        else
        {
            ch_printf(ch, "It is %s's turn.\n\r",
                      board->type == TYPE_LOCAL ?
                      GET_NAME((CHAR_DATA *)board->turn) :
                      (char *)board->turn);
        }
        return;
    }

    if ( !str_prefix(arg, "board") )
    {
        static char *b1;
        b1 = print_big_board(ch, ch->pcdata->game_board);
        send_to_char(b1,ch);
        return;
    }
    if ( !str_prefix(arg, "sboard") )
    {
        send_to_char(print_small_board(ch,ch->pcdata->game_board),ch);
        return;
    }

    if ( !ch->pcdata->game_board->player1 ||
         !ch->pcdata->game_board->player2 )
    {
        send_to_char("There is only 1 player.\n\r", ch);
        return;
    }
    if ( ch->pcdata->game_board->moves < 0 )
    {
        send_to_char("The game hasn't started yet.\n\r", ch);
        return;
    }

    if ( !str_prefix(arg, "promote") )
    {
        GAME_BOARD_DATA *board = ch->pcdata->game_board;
        int piece = board->board[board->lastx][board->lasty];
        char extra[MAX_INPUT_LENGTH];

        if (!((piece == BLACK_PAWN && board->player1 == ch) ||
              (piece == WHITE_PAWN && board->player2 == ch)))
        {
            send_to_char("You can't promote that.\n\r", ch);
            return;
        }

        if ((piece == BLACK_PAWN && board->lastx != 0) ||
            (piece == WHITE_PAWN && board->lastx != 7))
        {
            send_to_char("You can only promote pawns when they have reached theother end of the board.\n\r", ch);
            return;
        }

        if ( !argument || !*argument )
        {
            send_to_char("Promote it to what?\n\r", ch);
            return;
        }

        if ( !str_prefix(argument, "queen") )
        {
            if (IS_WHITE(piece))
                piece = WHITE_QUEEN;
            else
                piece = BLACK_QUEEN;
        }
        else if ( !str_prefix(argument, "bishop") )
        {
            if (IS_WHITE(piece))
                piece = WHITE_BISHOP;
            else
                piece = BLACK_BISHOP;
        }
        else if ( !str_prefix(argument, "knight") )
        {
            if (IS_WHITE(piece))
                piece = WHITE_KNIGHT;
            else
                piece = BLACK_KNIGHT;
        }
        else if ( !str_prefix(argument, "rook") )
        {
            if (IS_WHITE(piece))
                piece = WHITE_KROOK;
            else
                piece = BLACK_KROOK;
        }
        else
        {
            send_to_char("You can't promote it to that.\n\r", ch);
            return;
        }

        board->board[board->lastx][board->lasty] = piece;

        sprintf(extra, "%s (%c%d)",
                piece_names[piece], board->lastx+'a', board->lasty+1);

	board_move_messages(ch, MOVE_PROMOTE, extra);

#ifdef USE_IMC
	/* TODO: IMC support for promote */
	if ( ch->pcdata->game_board->type == TYPE_IMC)
	{
	    sprintf(arg, "promote %d%d %s", board->lastx, board->lasty, argument);
	    imc_send_chess((CHAR_DATA *)ch->pcdata->game_board->player1, (char *)ch->pcdata->game_board->player2, arg);
	}
#endif
        return;
    }

    if ( ch->pcdata->game_board->turn != ch )
    {
        send_to_char("It is not your turn.\n\r", ch);
        return;
    }

    if ( !str_prefix(arg, "castle") )
    {
        GAME_BOARD_DATA *board = ch->pcdata->game_board;
        int myx, rooky, kdy, rdy;

        if ( king_in_check(board,board->player1==ch?BLACK_KING:WHITE_KING) )
        {
            send_to_char("You cannot castle when in check.\n\r", ch);
            return;
        }

        if (board->player1 == ch)
        {
            if (board->moved[BLACK_KING] > 0)
            {
                send_to_char("You cannot castle if you have moved your king before.\n\r", ch);
                return;
            }
            myx = 7;
        }
        else
        {
            if (board->moved[WHITE_KING] > 0)
            {
                send_to_char("You cannot castle if you have moved your king before.\n\r", ch);
                return;
            }
            myx = 0;
        }

        if ( !*argument )
        {
            send_to_char("Usage: " SC_COMMANDNAME " castle <short|kingside|long|queenside>\n\r",ch);
            return;
        }

        if ( !str_prefix(argument, "short") || !str_prefix(argument, "kingside") )
        {
            if ((board->player1 == ch && board->moved[BLACK_KROOK] > 0) ||
                (board->player2 == ch && board->moved[WHITE_KROOK] > 0))
            {
                send_to_char("You cannot castle because you have moved your kingside rook before.\n\r", ch);
                return;
            }
            rooky = 7;
        }
        else if ( !str_prefix(argument, "long") || !str_prefix(argument, "queenside") )
        {
            if ((board->player1 == ch && board->moved[BLACK_QROOK] > 0) ||
                (board->player2 == ch && board->moved[WHITE_QROOK] > 0))
            {
                send_to_char("You cannot castle because you have moved your queenside rook before.\n\r", ch);
                return;
            }
            rooky = 0;
        }
        else
        {
            do_smaug_chess(ch, "castle");
            return;
        }

        if ((rooky == 7 &&
             (board->board[myx][6] != NO_PIECE ||
              board->board[myx][5] != NO_PIECE)) ||
            (rooky == 0 &&
             (board->board[myx][1] != NO_PIECE ||
              board->board[myx][2] != NO_PIECE ||
              board->board[myx][3] != NO_PIECE)))
        {
            send_to_char("There are pieces between the king and rook blocking your castle.\n\r", ch);
            return;
        }

        /* castling succeeded */
        if (board->board[myx][rooky] == BLACK_KROOK ||
            board->board[myx][rooky] == WHITE_KROOK)
        {
            kdy = 6;
            rdy = 5;
        }
        else if (board->board[myx][rooky] == BLACK_QROOK ||
                 board->board[myx][rooky] == WHITE_QROOK)
        {
            kdy = 2;
            rdy = 3;
        }
        else
        {
            bug("do_smaug_chess():chess.c board out of sync, game aborted, please contact maintainer");
            send_to_char("BUG in castling!  Aborting game!  Please contact an immortal.\n\r", ch);
            free_game(board);
            return;
        }

        /* check for 'move across check' rule */
        board->board[myx][rdy] = board->board[myx][4];
        board->board[myx][4] = NO_PIECE;

        if ( king_in_check(board,board->board[myx][rdy]) )
        {
            send_to_char("Your king would move across check if you castled.\n\r", ch);

            board->board[myx][4] = board->board[myx][rdy];
            board->board[myx][rdy] = NO_PIECE;
            return;
        }

        board->board[myx][kdy] = board->board[myx][rdy];
        board->board[myx][rdy] = board->board[myx][rooky];
        board->board[myx][rooky] = NO_PIECE;

        /* check for 'check' after castled */
        if ( king_in_check(board,board->board[myx][kdy]) )
        {
            send_to_char("Your king would be in check if you castled.\n\r", ch);

            board->board[myx][4] = board->board[myx][kdy];
            board->board[myx][kdy] = NO_PIECE;
            board->board[myx][rooky] = board->board[myx][rdy];
            board->board[myx][rdy] = NO_PIECE;
            return;
        }

        board->moved[board->board[myx][kdy]]++;
        board->moved[board->board[myx][rdy]]++;

	board_move_stuff(board);
	board_move_messages(ch, MOVE_CASTLE, rooky==7?"kingside":"queenside");
#ifdef USE_IMC
	/* TODO: IMC support for castle */
	if ( ch->pcdata->game_board->type == TYPE_IMC)
	{
	    sprintf(arg, "castle %s", rooky==7?"kingside":"queenside");
	    imc_send_chess((CHAR_DATA *)ch->pcdata->game_board->player1, (char *)ch->pcdata->game_board->player2, arg);
	}
#endif
    }
    else if ( !str_prefix(arg, "move") )
    {
        char extra[MAX_INPUT_LENGTH];
        char a,b;
        int x,y,dx,dy,ret;

        if ( !*argument )
        {
            send_to_char("Usage: " SC_COMMANDNAME " move <source> <destination>\n\r",ch);
            return;
        }

        if (sscanf(argument,"%c%d %c%d",&a,&y,&b,&dy)!=4)
        {
            send_to_char("Usage: " SC_COMMANDNAME " move <source> <destination>\n\r",ch);
            return;
        }
        argument = one_argument(argument, arg);
        argument = one_argument(argument, arg);

        if ( a < 'a' || a > 'h' || b < 'a' || b > 'h' || y < 1 || y > 8 || dy < 1 || dy > 8 )
        {
            send_to_char("Invalid move, use a-h and 1-8 (example: a4 b4).\n\r", ch);
            return;
        }

        x = a - 'a';
        dx = b - 'a';
        y--;
        dy--;

        extra[0] = '\0';

        ret = is_valid_move(ch,ch->pcdata->game_board,x,y,dx,dy);
        if (ret == MOVE_OK || ret == MOVE_TAKEN)
        {
            GAME_BOARD_DATA *board;
            int piece, destpiece;

            board = ch->pcdata->game_board;
            piece = board->board[x][y];
            destpiece = board->board[dx][dy];
            board->board[dx][dy] = piece;
            board->board[x][y] = NO_PIECE;
            if ( king_in_check(board,IS_WHITE(board->board[dx][dy])?WHITE_KING:BLACK_KING) )
            {
                board->board[dx][dy] = destpiece;
                board->board[x][y] = piece;
                ret = MOVE_INCHECK;
            }
            else
            {
                sprintf(extra, "%c%d (%s) to %c%d (%s)",
                        a, y+1, piece_names[piece],
                        b, dy+1, piece_names[destpiece]);

                board_move_stuff(board);
                board->lastx = dx;
                board->lasty = dy;
                board->moved[piece]++;
                board->moved[destpiece]++; /* taken pieces marked as moved */
#ifdef USE_IMC
                if ( ch->pcdata->game_board->type == TYPE_IMC)
                {
                    sprintf(arg, "move %d%d %d%d", x, y, dx, dy);
                    imc_send_chess((CHAR_DATA *)ch->pcdata->game_board->player1, (char *)ch->pcdata->game_board->player2, arg);
                }
#endif
            }
	    board_move_messages(ch, ret, extra);
        }

        if ( argument && *argument )
        {
            do_smaug_chess(ch, argument);
            return;
        }
        return;
    }

    do_smaug_chess(ch, "_syntax");
}

void init_chess(void)
{
#ifdef USE_IMC
    imc_register_packet_handler("chess", imc_recv_chess);
#endif
}