/*
 * cheng.c by Eric Dietz (c) 2005 Apr 23
 * take a chess position and return all legal moves out to a depth of DEPTH.
 */

/* defines */
#define VERSION "0.2"
/* To compile as chess bot:
 * #define CHESSBOT and #undef EVALUATOR
 * To compile as evaluator:
 * #define EVALUATOR and #undef CHESSBOT
 */
#define CHESSBOT
#undef EVALUATOR

/* includes */
#include <stdlib.h>           /* for malloc(), free() */
#include <stdio.h>            /* for printf(), getchar() */
#ifndef _WIN32
#include <unistd.h>
#include <netdb.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#else
#include <windows.h>
#include <winsock.h>
#include <io.h>
#endif // _WIN32
#include <sys/types.h>
#include <stdarg.h>
#include <errno.h>
#include <time.h>
#include <string.h>

/* platform-specific defines */
#ifdef _WIN32
#define SOCKET SOCKET
#define CLOSE_SOCK(fd) closesocket(fd) // on winsock2 include shutdown(fd, SD_BOTH)
#define SOCKLEN_T int
#define ERRNO WSAGetLastError()
#define P_EINTR         WSAEINTR
#define P_ENOTSOCK      WSAENOTSOCK
#define P_EWOULDBLOCK   WSAEWOULDBLOCK
#define P_ENOBUFS       WSAENOBUFS
#define P_ENOTCONN      WSAENOTCONN
#define P_EINPROGRESS   WSAEINPROGRESS
#define PERROR xperror
#define HERROR xperror
void xperror(char *s)
{
  char buf[128];
  FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, WSAGetLastError(), 0, buf, sizeof(buf), NULL);
  fprintf(stderr, "%s: %s", s, buf);
}
#else // _WIN32
#define SOCKET int
#define CLOSE_SOCK(fd) shutdown(fd, SHUT_RDWR); close(fd) // the shutdown() blasts the socket
#define SOCKLEN_T socklen_t
#define ERRNO errno
#define P_EINTR         EINTR
#define P_ENOTSOCK      ENOTSOCK
#define P_EWOULDBLOCK   EWOULDBLOCK
#define P_ENOBUFS       ENOBUFS
#define P_ENOTCONN      ENOTCONN
#define P_EINPROGRESS   EINPROGRESS
#define PERROR perror
#define HERROR herror
#endif // _WIN32

/* constants */
#define WHITE  1
#define BLACK -1
#define WHITE_PAWN    1
#define WHITE_KNIGHT  2
#define WHITE_BISHOP  3
#define WHITE_ROOK    4
#define WHITE_QUEEN   5
#define WHITE_KING    6
#define BLACK_PAWN   -1
#define BLACK_KNIGHT -2
#define BLACK_BISHOP -3
#define BLACK_ROOK   -4
#define BLACK_QUEEN  -5
#define BLACK_KING   -6
#define NOTHING       0
#define OVER_CHECKMATE    1
#define OVER_STALEMATE    2
#define OVER_LACKMATERIAL 3
#define DEPTH 2
#define NICK "chessbot"
#define SERVER "localhost"
#define PASSWORD "myslave"
#define PORT 7001
#define BUFFERSIZE 1024
#undef VERBOSE

/* chess board struct */
typedef struct chessboard chessboard;
struct chessboard
{
  int board[8][8];                      /* the board arrangement */
  int cas_wk, cas_wq, cas_bk, cas_bq;   /* castling flags */
  int en_pas;                           /* en passant flag */
  int turn;                             /* whos turn it is */
};

/* move tree struct */
typedef struct movetree movetree;
struct movetree
{
  int x1, y1, x2, y2;                   /* from and to coords */
  int promote;                          /* promotion flag */
  chessboard position;                  /* the position */
  movetree *next;                       /* pointer to next in list */
  movetree *down;                       /* pointer to next ply */
};

/* function declarations */
void socket_loop ();
void sendout (const char *format, ...);
void do_read ();
void do_send ();
void interpret (char *data);
void do_identify (char *data);
void do_full (char *data);
void do_ping (char *data);
void do_quit (char *data);
void do_okay (char *data);
void do_taken (char *data);
void do_joingame (char *data);
void do_joined (char *data);
void do_parted (char *data);
void do_sitdown (char *data);
void do_satdown (char *data);
void do_standup (char *data);
void do_stoodup (char *data);
void do_opponentwaiting (char *data);
void do_start (char *data);
void do_offerdraw (char *data);
void do_requestundo (char *data);
void do_requestcancel (char *data);
void do_winner (char *data);
void do_move (char *data);
void do_privmsg (char *data);
void do_illegal (char *data);
int eval (int argc, char *argv[]);
int readposition (movetree *moves, char *posstr);
void displayboard (chessboard board, int view);
void displaymoves (movetree *moves, int curdepth);
char ntop (int n);
int pton (char p);
void resetboard (chessboard *board);
void resetmoves (movetree *moves);
void clearmoves (movetree *moves);
void resetandclearmoves (movetree *moves);
int getmovetree (movetree *moves, int curdepth, int maxdepth);
int getsq (chessboard board, int x, int y);
void setsq (chessboard *board, int x, int y, int pc);
void domove (chessboard *board, int x1, int y1, int x2, int y2, int promote);
int islegal (chessboard board, int color, int x1, int y1, int x2, int y2);
int ischeck (chessboard board, int color);
int isgameover (chessboard board, int color);
int getmovelist (movetree *moves);
movetree *addmovetolist (movetree *moves, int num, chessboard board, int x1, int y1, int x2, int y2, int promote);

/* global vars */
movetree game;
char nick[128];
int table, color;
char host[128];
char pass[128];
char opnick[128];
int opcolor;
int port;
int clone;
SOCKET sockfd;
char readbuf[BUFFERSIZE];
char writebuf[BUFFERSIZE];
int rbf, wbf;
int hfd, cnct, slct, rcv, snd, nonb;
int running;
int antiidle;
struct sockaddr_in serv_addr;
struct hostent *server;
struct timeval timeout;
fd_set read_set, write_set;
#ifdef _WIN32
fd_set except_set;
WSADATA wsaData;
#else
int sockfl, pid;
#endif // _WIN32

/* entry point for EVALUATOR */
#ifndef CHESSBOT
int main (int argc, char *argv[])
#else // CHESSBOT
int eval (int argc, char *argv[])
#endif // CHESSBOT
{
  int depth;
  movetree *themoves;
  depth = DEPTH;
  themoves = malloc(sizeof(movetree));
  printf("Chess evaluator by Eric v%s depth=%d\n", VERSION, depth);
  if (argc > 1)
  {
    switch (readposition(themoves, argv[1]))
    {
    case 1:
      printf("illegal entry.\n");
      clearmoves(themoves);
      return 1;
    case 2:
      printf("** WARNING **: starting position is illegal.\n");
    }
  }
  else
  {
    printf("no command-line given; assuming default. generator:\n");
    printf("cheng RNBQKBNRPPPPPPPP................................pppppppprnbqkbnrKQkq.W\n");
    resetmoves(themoves);
  }
  displayboard(themoves->position, WHITE);
  getmovetree(themoves, 1, depth);
  displaymoves(themoves, 0);
  printf("\n\npress enter to continue.\n");
  getchar();
  clearmoves(themoves);
  return 0;
}

/* entry point for CHESSBOT */
#ifdef CHESSBOT
int main (int argc, char *argv[])
#else // CHESSBOT
int eval (int argc, char *argv[])
#endif // CHESSBOT
{
  memset(nick, 0, sizeof(nick));
  memset(pass, 0, sizeof(pass));
  strcpy(pass, PASSWORD);
  strcpy(opnick, "(noone)");
  table = 0;
  color = 0;
  opcolor = 0;
  running = 1;
  antiidle = 50;
  timeout.tv_sec = 10;
  timeout.tv_usec = 0;
#ifdef _WIN32
  if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0)
  {
    printf("unable to initialize winsock\n");
    return 1;
  }
#endif // _WIN32
  sockfd = socket(AF_INET, SOCK_STREAM, 0);
  hfd = (int)sockfd;
  memset(readbuf, 0, sizeof(readbuf));
  memset(writebuf, 0, sizeof(writebuf));
  rbf = 0;
  wbf = 0;
  if (sockfd == -1)
  {
    printf("error making socket\n");
    return 1;
  }
  if (argc < 2)
  {
    printf("no parameter given; using default\n");
    strcpy(host, SERVER);
    port = PORT;
    clone = 0;
  }
  else if (argc < 3)
  {
    strcpy(host, argv[1]);
    port = PORT;
    clone = 0;
  }
  else if (argc < 4)
  {
    strcpy(host, argv[1]);
    port = atoi(argv[2]);
    clone = 0;
  }
  else
  {
    strcpy(host, argv[1]);
    port = atoi(argv[2]);
    clone = atoi(argv[3]);
  }
  memset((char *)&serv_addr, 0, sizeof(serv_addr));
  serv_addr.sin_family = AF_INET;
  if ((serv_addr.sin_addr.s_addr = inet_addr(host)) == -1)
  {
    server = gethostbyname(host);
    if (server == NULL)
    {
      printf("error resolving server address: %s\n", host);
      return 1;
    }
    else
    {
      memcpy((char *)&serv_addr.sin_addr.s_addr,
             (char *)server->h_addr,
             server->h_length);
    }
  }
  serv_addr.sin_port = htons(port);
  srand((unsigned)time(NULL));
  printf("Chez " VERSION " chess bot, attempting to connect to %s[%s]:%i...\n",
   host, inet_ntoa(serv_addr.sin_addr), port );
  cnct = connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
  if (cnct == -1)
  {
    // we immediately got an error (probably connection refused from localhost)
    printf("Unable to connect to host: connection refused.\n");
    return 0;
  }
  else if (cnct == 0)
  {
    // we opened the socket instantly (probably connecting to localhost)
  }
#ifndef _WIN32
  if ((pid = fork()) < 0)
  {
    perror("fork");
    return 1;
  }
  else if (pid > 0)
  {
    printf("Connection succeeded; running in background with pid %i.\n", pid);
    return 0;
  }
#else
  printf("Connection succeeded...\n");
#endif // _WIN32
  socket_loop();
#ifdef _WIN32
  WSACleanup();
#endif // _WIN32
  CLOSE_SOCK(sockfd);
  return 0;
}

/* socket loop */
void socket_loop ()
{
  int res = 0;
  FD_ZERO(&read_set);
  FD_SET(sockfd, &read_set);
  FD_ZERO(&write_set);
  timeout.tv_sec = 10;
  timeout.tv_usec = 0;
  // loop forever!
  while (running)
  {
    // block poll for something to happen
    slct = select(hfd+1, &read_set, &write_set, NULL, &timeout);
    antiidle -= timeout.tv_sec;
    if (slct == -1)
    {
      // a select error occured...
      if
       (!(
        (ERRNO == P_EINTR) ||
        (ERRNO == P_ENOTSOCK) ))
      {
        res++;
        if (res > 5)
        {
          PERROR("select");
          exit(1);
        }
      }
      else
      {
      }
    }
    else if (slct == 0)
    {
      // just means select() timed out....
      timeout.tv_sec = 10;
      timeout.tv_usec = 0;
    }
    else
    {
      // something happened
      if (FD_ISSET(sockfd, &read_set))
      {
        // client is waiting to speak
        do_read();
      }
      if (FD_ISSET(sockfd, &write_set))
      {
        // client buffer is ready for writing...
        do_send();
      }
    }
    // anti idle...
    if (antiidle <= 0)
    {
      sendout("UPTI");
      antiidle = 50;
    }
    // add clients back to list of select()'s FD_SET
    FD_ZERO(&read_set);
    FD_ZERO(&write_set);
    FD_SET(sockfd, &read_set);
    if (wbf > 0)
      FD_SET(sockfd, &write_set);
  }
}

/* send data to the server */
void sendout (const char *format, ...)
{
  char vbuf[1024];
	va_list vl;
  vbuf[0] = '\0';
	va_start(vl, format);
  vsprintf(vbuf, format, vl);
	va_end(vl);
  if (strlen(vbuf) + strlen(writebuf) > sizeof(writebuf) - 3)
  {
    // sendqueue exceeded...
    exit(1);
  }
  strcat(writebuf, vbuf);
  strcat(writebuf, "\r\n");
  wbf = (int)strlen(writebuf);
#ifdef VERBOSE
  printf("sent: %s\n", vbuf);
#endif // VERBOSE
  do_send();
}

/* read data from the server to the buffer */
void do_read ()
{
  char vbuf[1024];
  int i;
  vbuf[0] = '\0';
  rcv = recv(sockfd, vbuf, sizeof(vbuf)-1, 0);
  if (rcv == -1)
  {
    // read error: connection reset by peer...
    exit(1);
  }
  else if (rcv == 0)
  {
    // socket closed...
    exit(0);
  }
  else
  {
    if (rbf + rcv + 1 > sizeof(readbuf))
    {
      // receivequeue exceeded...
      exit(1);
    }
    vbuf[rcv] = '\0';
    strcat(readbuf, vbuf);
    rbf = (int)strlen(readbuf);
    vbuf[0] = '\0';
    for (i = 0; i < rbf; i++)
    {
      if (readbuf[i] == '\n')
      {
        readbuf[i] = '\0';
        interpret(readbuf);
        strcpy(vbuf, readbuf+i+1);
        memset(readbuf,0,sizeof(readbuf));
        strcpy(readbuf, vbuf);
        rbf = (int)strlen(readbuf);
        i = -1;
      }
    }
  }
}

/* send data from the bugffer to the server */
void do_send ()
{
  snd = send(sockfd, writebuf, wbf, 0);
  if (snd <= 0)
  // some error...
  {
    if
     (
      (ERRNO == P_EWOULDBLOCK) ||
#ifndef _WIN32
      (ERRNO == EAGAIN) ||
#endif // _WIN32
      (ERRNO == P_ENOBUFS) )
    {
      snd = 0;
    }
    else
    {
      // write error: connection reset by peer...
      exit(1);
    }
  }
  wbf -= snd;
  if (snd > 0)
    strcpy(writebuf, writebuf + snd);
  if (wbf < 0)
    wbf = 0;
  if (wbf > 0 && !FD_ISSET(sockfd, &write_set))
    FD_SET(sockfd, &write_set);
}

/* interpret a command */
void interpret (char *data)
{
  char *parms = data+5;
#ifdef VERBOSE
  printf("read: %s\n", data);
#endif // VERBOSE
  if      (!strncmp(data,"IDNT",4)) { do_identify(parms); }
  else if (!strncmp(data,"FULL",4)) { do_full(parms); }
  else if (!strncmp(data,"PING",4)) { do_ping(parms); }
  else if (!strncmp(data,"QUIT",4)) { do_quit(parms); }
  else if (!strncmp(data,"OKAY",4)) { do_okay(parms); }
  else if (!strncmp(data,"TAKN",4)) { do_taken(parms); }
  else if (!strncmp(data,"JOIN",4)) { do_joingame(parms); }
  else if (!strncmp(data,"JOND",4)) { do_joined(parms); }
  else if (!strncmp(data,"PRTD",4)) { do_parted(parms); }
  else if (!strncmp(data,"SITD",4)) { do_sitdown(parms); }
  else if (!strncmp(data,"SATD",4)) { do_satdown(parms); }
  else if (!strncmp(data,"STND",4)) { do_standup(parms); }
  else if (!strncmp(data,"STOD",4)) { do_stoodup(parms); }
  else if (!strncmp(data,"OWTN",4)) { do_opponentwaiting(parms); }
  else if (!strncmp(data,"STRT",4)) { do_start(parms); }
  else if (!strncmp(data,"ODRW",4)) { do_offerdraw(parms); }
  else if (!strncmp(data,"RUND",4)) { do_requestundo(parms); }
  else if (!strncmp(data,"RCNL",4)) { do_requestcancel(parms); }
  else if (!strncmp(data,"WINR",4)) { do_winner(parms); }
  else if (!strncmp(data,"MOVE",4)) { do_move(parms); }
  else if (!strncmp(data,"PMSG",4)) { do_privmsg(parms); }
  else if (!strncmp(data,"ILGL",4)) { do_illegal(parms); }
}

/* identify to the server */
void do_identify(char *data)
{
  strcpy(nick, NICK);
  sendout("USER %s", nick);
}

/* server is full; quit */
void do_full(char *data)
{
  printf("server is full :(\n");
  exit(1);
}

/* avoid ping / idle timeouts */
void do_ping(char *data)
{
  sendout("PONG %s", data);
}

/* disconnect */
void do_quit(char *data)
{
  exit(1);
}

/* logged in; take a table */
void do_okay(char *data)
{
  sendout("CREA");
}

/* nickname taken; try again */
void do_taken(char *data)
{
  if (clone)
  {
    if (strlen(nick) + 2 > sizeof(nick))
      exit(1);
    strcat(nick, "_");
    sendout("USER %s", nick);
  }
  else
  {
    exit(1);
  }
}

/* joined a table */
void do_joingame(char *data)
{
  table = atoi(data);
}

/* someone else joined the table; talk to them */
void do_joined(char *data)
{
  int t = atoi(data);
  if (t == table)
  {
    sendout("PRIV %d 1", table);
    sendout("MESG %d Hello I am a chess bot.  If you'd like to play a game,", table);
    sendout("MESG %d just take a seat at whatever color you'd like, and hit start.", table);
    sendout("MESG %d Note: I am very early in development so", table);
    sendout("MESG %d it should be very easy to beat me.", table);
    sendout("MESG %d Last opponent: %s%s.",
     table, opnick, opcolor==WHITE?" (white)":opcolor==BLACK?" (black)":"" );
    sendout("INVT %d %s", table, data+2+(t>99?2:t>9?1:0));
  }
}

/* someone else parted the table */
void do_parted(char *data)
{
  int t = atoi(data);
  if (t == table)
  {
    sendout("PRIV %d 0", table);
    sendout("STND %d", table);
    color = 0;
  }
}

/* sat at the table */
void do_sitdown(char *data)
{
  int t = atoi(data);
  if (t == table)
  {
    color = (!strncmp(data+2+(t>99?2:t>9?1:0),"W",1)?WHITE:BLACK);
  }
}

/* someone else sat at the table */
void do_satdown(char *data)
{
  int t = atoi(data);
  int n = 0;
  if (t == table)
  {
    n = (t>99?2:t>9?1:0);
    if (!strncmp(data+n+2,"W",1))
    {
      sendout("SITD %d B", table);
      opcolor = WHITE;
    }
    else
    {
      sendout("SITD %d W", table);
      opcolor = BLACK;
    }
    strncpy(opnick, data+n+4, sizeof(opnick));
    opnick[sizeof(opnick)-1] = '\0';
  }
}

/* stood up from the table */
void do_standup(char *data)
{
  int t = atoi(data);
  if (t == table)
  {
    color = 0;
  }
}

/* someone else stood up from the table */
void do_stoodup(char *data)
{
  int t = atoi(data);
  if (t == table)
  {
    sendout("STND %d", table);
    sendout("INVT %d %s", table, data+2+(t>99?2:t>9?1:0));
  }
}

/* opponent waiting */
void do_opponentwaiting(char *data)
{
  sendout("STRT %d", table);
}

/* game has begun */
void do_start(char *data)
{
  int legal, which, i;
  movetree *ourmove;
  resetmoves(&game);
  if (color == WHITE)
  {
    // my turn so let's give'er a go...
    legal = getmovetree(&game,1,1);
    if (legal > 0)
    {
      which = rand() % legal + 1;
      i = 1;
      for (ourmove = game.down; ourmove && i < which; ourmove = ourmove->next)
      {
        i++;
      }
      sendout
      ("MOVE %d %c%c%c%c%c",
       table,
       (char)ourmove->x1+'`',
       (char)ourmove->y1+'0',
       (char)ourmove->x2+'`',
       (char)ourmove->y2+'0',
       ntop(ourmove->promote) );
      clearmoves(game.down);
    }

  }
}

/* opponent offered a draw */
void do_offerdraw(char *data)
{
  //
}

/* opponent requested undo */
void do_requestundo(char *data)
{
  //
}

/* opponent requested cancel */
void do_requestcancel(char *data)
{
  sendout("CNCL %d", table);
}

/* game over */
void do_winner(char *data)
{
  sendout("MESG %d YOU BASTARD!!!", table);
}


/* move made */
void do_move(char *data)
{
  int x1, y1, x2, y2, promote;
  int legal, which, i;
  movetree *ourmove;
  char *c;
  for (c = data; *c != ' ' && *c; c++) ;
  c++;
  x1 = *c - '@';
  c++;
  y1 = *c - '0';
  c++;
  x2 = *c - '@';
  c++;
  y2 = *c - '0';
  c++;
  if (c)
    promote = pton(*c);
  else
    promote = 0;
  domove(&game.position, x1, y1, x2, y2, promote);
  if (game.position.turn == color)
  {
    legal = getmovetree(&game,1,1);
    if (legal > 0)
    {
      which = rand() % legal + 1;
      i = 1;
      for (ourmove = game.down; ourmove && i < which; ourmove = ourmove->next)
      {
        i++;
      }
      sendout
      ("MOVE %d %c%c%c%c%c",
       table,
       (char)ourmove->x1+'`',
       (char)ourmove->y1+'0',
       (char)ourmove->x2+'`',
       (char)ourmove->y2+'0',
       ntop(ourmove->promote) );
      clearmoves(game.down);
    }
  }
}

/* process private messages sent to the bot */
void do_privmsg (char *data)
{
  char *cmd;
  for (cmd = data; *cmd != ' ' && *cmd; cmd++) ;
  cmd++;
  if (!strncmp(cmd, pass, strlen(pass)))
  {
    cmd += strlen(pass) + 1;
    if (!strncmp(cmd, "show", 4))
    {
      displayboard(game.position, color);
    }
    else
    {
      printf("Notice: sending command: %s\n", cmd);
      sendout("%s", cmd);
    }
  }
}

/* take note of illegal move errors... */
void do_illegal (char *data)
{
  printf("Notice: server rejected move as illegal: %s\n", data);
}

/* parse input string to generate the starting position, and make sure it's a legal position */
int readposition (movetree *moves, char *posstr)
{
  int wp=0,wn=0,wb=0,wr=0,wq=0,wk=0,bp=0,bn=0,bb=0,br=0,bq=0,bk=0,none=0;
  int wbl=0,wbd=0,bbl=0,bbd=0;
  int i,j;
  int legalpos = 1, piece = 0;
  resetmoves(moves);
  if (strlen(posstr) < 70)
    return 1;
  for (i=0; i<64; i++)
    if (pton(posstr[i]) == NOTHING && posstr[i] != '.')
      return 1;
  if
  ((posstr[64] != 'K' && posstr[64] != '.') ||
   (posstr[65] != 'Q' && posstr[65] != '.') ||
   (posstr[66] != 'k' && posstr[66] != '.') ||
   (posstr[67] != 'q' && posstr[67] != '.') ||
   !((posstr[68] >= 'a' && posstr[68] <= 'h') ||
     (posstr[68] >= 'A' && posstr[68] <= 'H') ||
     posstr[68] == '.') ||
   (posstr[69] != 'W' && posstr[69] != 'B' &&
    posstr[69] != 'w' && posstr[69] != 'b') )
     return 1;
  for (i=0; i<8; i++)
  {
    for (j=0; j<8; j++)
    {
      piece = pton(posstr[i*8+j]);
      switch (piece)
      {
      case WHITE_PAWN:
        wp++;
        if (i == 0 || i == 7) legalpos = 0;
        break;
      case BLACK_PAWN:
        bp++;
        if (i == 0 || i == 7) legalpos = 0;
        break;
      case WHITE_KNIGHT:
        wn++;
        break;
      case BLACK_KNIGHT:
        bn++;
        break;
      case WHITE_BISHOP:
        wb++;
        if ((i+j) % 2) wbl++; else wbd++;
        break;
      case BLACK_BISHOP:
        bb++;
        if ((i+j) % 2) bbl++; else bbd++;
        break;
      case WHITE_ROOK:
        wr++;
        break;
      case BLACK_ROOK:
        br++;
        break;
      case WHITE_QUEEN:
        wq++;
        break;
      case BLACK_QUEEN:
        bq++;
        break;
      case WHITE_KING:
        wk++;
        break;
      case BLACK_KING:
        bk++;
        break;
      case NOTHING:
        none++;
        break;
      }
      moves->position.board[j][i] = piece;
    }
  }
  if (posstr[64] != 'K')
    moves->position.cas_wk = 0;
  if (posstr[65] != 'Q')
    moves->position.cas_wq = 0;
  if (posstr[66] != 'k')
    moves->position.cas_bk = 0;
  if (posstr[67] != 'q')
    moves->position.cas_bq = 0;
  if (posstr[68] >= 'a' && posstr[68] <= 'h')
    moves->position.en_pas = posstr[68]-'a';
  if (posstr[68] >= 'A' && posstr[68] <= 'H')
    moves->position.en_pas = posstr[68]-'A';
  if      (posstr[69] == 'W' || posstr[69] == 'w')
    moves->position.turn = WHITE;
  else if (posstr[69] == 'B' || posstr[69] == 'b')
    moves->position.turn = BLACK;
  else
    return 1;
  if (wk != 1 || bk != 1 || none < 32)
    legalpos = 0;
  if (wp > 8 || bp > 8 || wp+wn+wb+wr+wq > 15 || bp+bn+bb+br+bq > 15)
    legalpos = 0;
  if ((wp == 8 && (wbl > 1 || wbd > 1)) || (bp == 8 && (bbl > 1 || bbd > 1)) )
    legalpos = 0;
  if
  ((moves->position.turn == WHITE && ischeck(moves->position, BLACK)) ||
   (moves->position.turn == BLACK && ischeck(moves->position, WHITE)) )
    legalpos = 0;
  if (!legalpos)
    return 2;
  return 0;
}

/* display a board */
void displayboard (chessboard board, int view)
{
  int i;
  int over, chck;
  printf("board:\n");
  printf("castle=%c%c%c%c en_pas=%c\n",
   board.cas_wk?'K':'.', board.cas_wq?'Q':'.',
   board.cas_bk?'k':'.', board.cas_bq?'q':'.',
   board.en_pas?(char)board.en_pas+'a':'.' );
  if (view == BLACK)
  {
    printf("white\n");
    for (i=0; i <= 7; i++)
      printf("%i %c %c %c %c %c %c %c %c\n", i+1,
       ntop(board.board[7][i]), ntop(board.board[6][i]),
       ntop(board.board[5][i]), ntop(board.board[4][i]),
       ntop(board.board[3][i]), ntop(board.board[2][i]),
       ntop(board.board[1][i]), ntop(board.board[0][i]) );
    printf("  H G F E D C B A\n");
    printf("black\n");
  }
  else
  {
    printf("black\n");
    for (i=7; i >= 0; i--)
      printf("%i %c %c %c %c %c %c %c %c\n", i+1,
       ntop(board.board[0][i]), ntop(board.board[1][i]),
       ntop(board.board[2][i]), ntop(board.board[3][i]),
       ntop(board.board[4][i]), ntop(board.board[5][i]),
       ntop(board.board[6][i]), ntop(board.board[7][i]) );
    printf("  A B C D E F G H\n");
    printf("white\n");
  }
  over = isgameover(board,board.turn);
  chck = ischeck(board,board.turn);
  printf("turn=%c    %s\n",board.turn==WHITE?'W':'B',
  (over==OVER_CHECKMATE?"checkmate":
   over==OVER_STALEMATE?"stalemate":
   over==OVER_LACKMATERIAL?"lack-of-material":
   chck?"check":"")
  );
}

/* display a move list */
void displaymoves (movetree *moves, int curdepth)
{
  movetree *curmoves;
  int over, chck;
  for (curmoves = moves->down; curmoves; curmoves = curmoves->next)
  {
    over = isgameover(curmoves->position,curmoves->position.turn);
    chck = ischeck(curmoves->position,curmoves->position.turn);
    if (curdepth <= 0)
      printf("\n");
    printf
    ("%c%c%c%c%c%c",
     (char)curmoves->x1+'`',(char)curmoves->y1+'0',(char)curmoves->x2+'`',(char)curmoves->y2+'0',
     ntop(curmoves->promote),
     (over==OVER_CHECKMATE?'#':(over==OVER_STALEMATE||over==OVER_LACKMATERIAL)?'=':chck?'+':'.') );
    if (curdepth >= 1)
      printf(" ");
    else
      printf(" -> ");
    displaymoves(curmoves, curdepth+1);
  }
}

/* convert a piece number to character */
char ntop (int n)
{
  switch (n)
  {
  case WHITE_KING:   return 'K';
  case BLACK_KING:   return 'k';
  case WHITE_QUEEN:  return 'Q';
  case BLACK_QUEEN:  return 'q';
  case WHITE_ROOK:   return 'R';
  case BLACK_ROOK:   return 'r';
  case WHITE_BISHOP: return 'B';
  case BLACK_BISHOP: return 'b';
  case WHITE_KNIGHT: return 'N';
  case BLACK_KNIGHT: return 'n';
  case WHITE_PAWN:   return 'P';
  case BLACK_PAWN:   return 'p';
  default: return '.';
  }
}

/* convert a piece character to number */
int pton (char p)
{
  switch (p)
  {
  case 'K': return WHITE_KING;
  case 'k': return BLACK_KING;
  case 'Q': return WHITE_QUEEN;
  case 'q': return BLACK_QUEEN;
  case 'R': return WHITE_ROOK;
  case 'r': return BLACK_ROOK;
  case 'B': return WHITE_BISHOP;
  case 'b': return BLACK_BISHOP;
  case 'N': return WHITE_KNIGHT;
  case 'n': return BLACK_KNIGHT;
  case 'P': return WHITE_PAWN;
  case 'p': return BLACK_PAWN;
  default: return NOTHING;
  }
}

/* reset a chess board */
void resetboard (chessboard *board)
{
  int i, j;
  for (i=0; i<8; i++)
    for (j=2; j<6; j++)
      board->board[i][j] = NOTHING;
  for (i=0; i<8; i++)
  {
    board->board[i][1] = WHITE_PAWN;
    board->board[i][6] = BLACK_PAWN;
  }
  board->board[0][0] = WHITE_ROOK;
  board->board[1][0] = WHITE_KNIGHT;
  board->board[2][0] = WHITE_BISHOP;
  board->board[3][0] = WHITE_QUEEN;
  board->board[4][0] = WHITE_KING;
  board->board[5][0] = WHITE_BISHOP;
  board->board[6][0] = WHITE_KNIGHT;
  board->board[7][0] = WHITE_ROOK;
  board->board[0][7] = BLACK_ROOK;
  board->board[1][7] = BLACK_KNIGHT;
  board->board[2][7] = BLACK_BISHOP;
  board->board[3][7] = BLACK_QUEEN;
  board->board[4][7] = BLACK_KING;
  board->board[5][7] = BLACK_BISHOP;
  board->board[6][7] = BLACK_KNIGHT;
  board->board[7][7] = BLACK_ROOK;
  board->cas_wk = 1; board->cas_wq = 1;
  board->cas_bk = 1; board->cas_bq = 1;
  board->en_pas = 0;
  board->turn = WHITE;
}

/* reset a move list */
void resetmoves (movetree *moves)
{
  moves->x1 = 0;
  moves->y1 = 0;
  moves->x2 = 0;
  moves->y2 = 0;
  resetboard(&moves->position);
  moves->next = NULL;
  moves->down = NULL;
}

/* clear a move list */
void clearmoves (movetree *moves)
{
  if (moves)
  {
    clearmoves(moves->next);
    clearmoves(moves->down);
    free(moves);
  }
}

/* reset and clear a move list */
void resetandclearmoves (movetree *moves)
{
  if (moves)
  {
    clearmoves(moves->next);
    clearmoves(moves->down);
    resetmoves(moves);
  }
}

/* get a move tree */
int getmovetree (movetree *moves, int curdepth, int maxdepth)
{
  int ret;
  movetree *curmoves;
  ret = getmovelist(moves);
  if (curdepth >= maxdepth)
    return ret;
  //for (curmoves = moves->next; curmoves; curmoves = curmoves->down)
  for (curmoves = moves->down; curmoves; curmoves = curmoves->next)
    getmovetree(curmoves, curdepth + 1, maxdepth);
  return ret;
}

/* return the piece at a certain square */
int getsq (chessboard board, int x, int y)
{
  if (x < 1 || x > 8 || y < 1 || y > 8)
    return NOTHING;
  return board.board[x-1][y-1];
}

/* put a piece at a certain square */
void setsq (chessboard *board, int x, int y, int pc)
{
  if (x < 1 || x > 8 || y < 1 || y > 8)
    return;
  board->board[x-1][y-1] = pc;
}

/* do a given move */
void domove (chessboard *board, int x1, int y1, int x2, int y2, int promote)
{
  int sq;
  sq = getsq(*board,x1,y1);
  board->en_pas = 0;
  switch (sq)
  {
  case WHITE_PAWN:
    if      (x1 != x2 && getsq(*board,x2,y2) == NOTHING)
      setsq(board,x2,5,NOTHING);        /* capturing en passant */
    else if (y1 == 2 && y2 == 4)
      board->en_pas = x1;               /* moving 2 squares; set en_pas flag */
    else if (y2 == 8)
    {                                   /* promoting pawn */
      if (promote == NOTHING)
        promote = WHITE_QUEEN;
      else if (promote < NOTHING)
        promote = NOTHING+NOTHING-promote;
      sq = promote;
    }
    break;
  case BLACK_PAWN:
    if      (x1 != x2 && getsq(*board,x2,y2) == NOTHING)
      setsq(board,x2,4,NOTHING);        /* capturing en passant */
    else if (y1 == 7 && y2 == 5)
      board->en_pas = x1;               /* moving 2 squares; set en_pas flag */
    else if (y2 == 1)
    {                                   /* promoting pawn */
      if (promote == NOTHING)
        promote = BLACK_QUEEN;
      else if (promote > NOTHING)
        promote = NOTHING+NOTHING-promote;
      sq = promote;
    }
    break;
  case WHITE_ROOK:
    if      (x1 == 1 && y1 == 1 && board->cas_wq)
      board->cas_wq = 0;                /* clear white queenside castling flag */
    else if (x1 == 8 && y1 == 1 && board->cas_wk)
      board->cas_wk = 0;                /* clear white kingside castling flag */
    break;
  case BLACK_ROOK:
    if      (x1 == 8 && y1 == 8 && board->cas_bk)
      board->cas_bk = 0;                /* clear black kingside castling flag */
    else if (x1 == 1 && y1 == 8 && board->cas_bq)
      board->cas_bq = 0;                /* clear black queenside castling flag */
    break;
  case WHITE_KING:
    if (board->cas_wk)
      board->cas_wk = 0;                /* clear white kingside castling flag */
    if (board->cas_wq)
      board->cas_wq = 0;                /* clear white queenside castling flag */
    if      (x1 == 5 && x2 == 7)
    {                                   /* castling kingside */
      setsq(board,8,1,NOTHING);
      setsq(board,6,1,WHITE_ROOK);
    }
    else if (x1 == 5 && x2 == 3)
    {                                   /* castling queenside */
      setsq(board,1,1,NOTHING);
      setsq(board,4,1,WHITE_ROOK);
    }
    break;
  case BLACK_KING:
    if (board->cas_bk)
      board->cas_bk = 0;                /* clear black kingside castling flag */
    if (board->cas_bq)
      board->cas_bq = 0;                /* clear black queenside castling flag */
    if      (x1 == 5 && x2 == 7)
    {                                   /* castling kingside */
      setsq(board,8,8,NOTHING);
      setsq(board,6,8,BLACK_ROOK);
    }
    else if (x1 == 5 && x2 == 3)
    {                                   /* castling queenside */
      setsq(board,1,8,NOTHING);
      setsq(board,4,8,BLACK_ROOK);
    }
    break;
  }
  setsq(board,x2,y2,sq);                /* and move the piece... */
  setsq(board,x1,y1,NOTHING);
  if (board->turn == WHITE)             /* and set it to the other players' turn */
    board->turn = BLACK;
  else
    board->turn = WHITE;
}

/* check legality of a given move */
int islegal (chessboard board, int color, int x1, int y1, int x2, int y2)
{
  int sq, sq2;                          /* from-square, to-square */
  int dx=0, dy=0, h, k;                 /* iterators for bishop/rook/queen move checks */
  chessboard temp;                      /* temp board (to test for check) */
  if                                    /* some obvious signs that a move is illegal... */
  ((x1 < 1 || x1 > 8 || y1 < 1 || y1 > 8 || x2 < 1 || x2 > 8 || y2 < 1 || y2 > 8) || /* cant move off the board */
   (x1 == x2 && y1 == y2) ||                                    /* cant move a piece to where it already is */
   ((color == WHITE && (getsq(board,x1,y1) <= NOTHING || getsq(board,x2,y2) > NOTHING)) || /* cant move opponents piece */
    (color == BLACK && (getsq(board,x1,y1) >= NOTHING || getsq(board,x2,y2) < NOTHING)) ) ) /* or capture your own piece */
    return 0;
  sq = getsq(board,x1,y1);
  sq2 = getsq(board,x2,y2);
  temp = board;
  switch (sq)
  {
  case WHITE_PAWN:
    if
    (x2 - x1 < -1 || x2 - x1 > 1)       /* cant jump over files */
      return 0;
    if (x1 == x2)
    {
      if
      ((sq2 != NOTHING) ||              /* cant capture forward */
       (y2 - y1 > 2 || y2 - y1 < 1) ||  /* can only move 1 or 2 spaces */
       (y2 - y1 == 2 && (!(y1 == 2 && y2 == 4) || getsq(board,x1,3) != NOTHING)) ) /* cant jump over pieces */
        return 0;
    }
    else
    {
      if (y2 - y1 != 1)                /* captures must be diagonal */
        return 0;
      if (sq2 == NOTHING)
      {
        if
        ((board.en_pas != x2) ||        /* can only do en passant if the flag is set */
         (!(y1 == 5 && y2 == 6)) ||     /* can only do en passant on the 6th rank */
         (getsq(board,x2,5) != BLACK_PAWN) ) /* can only do en passant if taking opposing pawn */
          return 0;
        else
          setsq(&temp,x2,5,NOTHING);    /* capturing en passant OK */
      }
    }
    if (y2 == 8)                        /* promoting a pawn */
      sq = WHITE_QUEEN;
    break;
  case BLACK_PAWN:
    if
    (x2 - x1 < -1 || x2 - x1 > 1)       /* cant jump over files */
      return 0;
    if (x1 == x2)
    {
      if
      ((sq2 != NOTHING) ||              /* cant capture forward */
       (y2 - y1 < -2 || y2 - y1 > -1) ||/* can only move 1 or 2 spaces */
       (y2 - y1 == -2 && (!(y1 == 7 && y2 == 5) || getsq(board,x1,6) != NOTHING)) ) /* cant jump over pieces */
        return 0;
    }
    else
    {
      if (y2 - y1 != -1)                /* captures must be diagonal */
        return 0;
      if (sq2 == NOTHING)
      {
        if
        ((board.en_pas != x2) ||        /* can only do en passant if the flag is set */
         (!(y1 == 4 && y2 == 3)) ||     /* can only do en passant on the 6th rank */
         (getsq(board,x2,4) != WHITE_PAWN) ) /* can only do en passant if taking opposing pawn */
          return 0;
        else
          setsq(&temp,x2,4,NOTHING);    /* capturing en passant OK */
      }
    }
    if (y2 == 1)                        /* promoting a pawn */
      sq = BLACK_QUEEN;
    break;
  case WHITE_KNIGHT:
  case BLACK_KNIGHT:
    if
    ((x1 == x2 || y1 == y2) ||          /* must be L shaped move */
     ((y2>y1?y2-y1:y1-y2) + (x2>x1?x2-x1:x1-x2) != 3) ) /* must be 2 in one dir and 1 in another */
      return 0;
    break;
  case WHITE_QUEEN:                     /* note: queen move test is embedded in Bishop+Rook tests... */
  case BLACK_QUEEN:
  case WHITE_BISHOP:
  case BLACK_BISHOP:
    if ((x2>x1?x2-x1:x1-x2) != (y2>y1?y2-y1:y1-y2)) /* must be on a diagonal */
    {
      if (sq != WHITE_QUEEN && sq != BLACK_QUEEN)
        return 0;
    }
    else
    {
      if (x2 > x1) dx = 1; else dx = -1;
      if (y2 > y1) dy = 1; else dy = -1;
      h = x1 + dx;
      k = y1 + dy;
      while (!(h == x2 && k == y2) && h >= 1 && h <= 8 && k >= 1 && k <= 8)
      {
        if (getsq(board,h,k) != NOTHING) /* found something in the way (cant jump) */
          return 0;
        h += dx;
        k += dy;
      }
    }
    if (sq != WHITE_QUEEN && sq != BLACK_QUEEN)
      break;
  case WHITE_ROOK:
  case BLACK_ROOK:
    if (!(x1 == x2 || y1 == y2))        /* must be along a rank or file */
    {
      if (sq != WHITE_QUEEN && sq != BLACK_QUEEN)
        return 0;
    }
    else
    {
      if (x2 > x1) dx = 1; else if (x2 < x1) dx = -1; else dx = 0;
      if (y2 > y1) dy = 1; else if (y2 < y1) dy = -1; else dy = 0;
      h = x1 + dx;
      k = y1 + dy;
      while (!(h == x2 && k == y2) && h >= 1 && h <= 8 && k >= 1 && k <= 8)
      {
        if (getsq(board,h,k) != NOTHING) /* found something in the way (cant jump) */
          return 0;
        h += dx;
        k += dy;
      }
    }
    break;
  case WHITE_KING:
    if (x2 - x1 < -1 || x2 - x1 > 1 || y2 - y1 < -1 || y2 - y1 > 1) /* only move 1 square, or... */
    {
      if
      ((y1 != 1 || y2 != 1 || x1 != 5 || (x2 != 3 && x2 != 7)) || /* can only castle these coords */
       (ischeck(board,WHITE)) ||        /* cant castle out of check */
       ((x2 == 3 && (!board.cas_wk ||   /* cant castle if illegal or something in the way ... */
         getsq(board,4,1) != NOTHING || getsq(board,3,1) != NOTHING || getsq(board,2,1) != NOTHING )) ||
        (x2 == 7 && (!board.cas_wq ||
         getsq(board,6,1) != NOTHING || getsq(board,7,1) != NOTHING ))) )
      return 0;
      if (x2 == 3)
      {
        setsq(&temp,4,1,WHITE_KING);
        setsq(&temp,5,1,NOTHING);
        if (ischeck(temp,WHITE))        /* cant castle through check */
          return 0;
        setsq(&temp,4,1,WHITE_ROOK);    /* castling white queenside OK */
        setsq(&temp,1,1,NOTHING);
      }
      else
      {
        setsq(&temp,6,1,WHITE_KING);
        setsq(&temp,5,1,NOTHING);
        if (ischeck(temp,WHITE))        /* cant castle through check */
          return 0;
        setsq(&temp,6,1,WHITE_ROOK);    /* castling white kingside OK */
        setsq(&temp,8,1,NOTHING);
      }
    }
    break;
  case BLACK_KING:
    if (x2 - x1 < -1 || x2 - x1 > 1 || y2 - y1 < -1 || y2 - y1 > 1) /* only move 1 square, or... */
    {
      if
      ((y1 != 8 || y2 != 8 || x1 != 5 || (x2 != 3 && x2 != 7)) || /* can only castle these coords */
       (ischeck(board,BLACK)) ||        /* cant castle out of check */
       ((x2 == 3 && (!board.cas_bk ||   /* cant castle if illegal or something in the way ... */
         getsq(board,4,8) != NOTHING || getsq(board,3,8) != NOTHING || getsq(board,2,8) != NOTHING )) ||
        (x2 == 7 && (!board.cas_bq ||
         getsq(board,6,8) != NOTHING || getsq(board,7,8) != NOTHING ))) )
      return 0;
      if (x2 == 3)
      {
        setsq(&temp,4,8,BLACK_KING);
        setsq(&temp,5,8,NOTHING);
        if (ischeck(temp,BLACK))        /* cant castle through check */
          return 0;
        setsq(&temp,4,8,BLACK_ROOK);    /* castling black queenside OK */
        setsq(&temp,1,8,NOTHING);
      }
      else
      {
        setsq(&temp,6,8,BLACK_KING);
        setsq(&temp,5,8,NOTHING);
        if (ischeck(temp,BLACK))        /* cant castle through check */
          return 0;
        setsq(&temp,6,8,BLACK_ROOK);    /* castling black kingside OK */
        setsq(&temp,8,8,NOTHING);
      }
    }
    break;
  }
  setsq(&temp,x2,y2,sq);                /* try the move... */
  setsq(&temp,x1,y1,NOTHING);
  if (ischeck(temp,color))
    return 0;                           /* move is illegal if it puts player in check */
  return 1;
}

/* see if a given position is check */
int ischeck (chessboard board, int color)
{
  int i, j;                             /* iterators */
  int kx, ky;                           /* location of king */
  int op,on,ob,or,oq,ok;                /* representation of opposing pawn,knight,bishop,rook,queen,king */
  int dx=0, dy=0, h, k;                 /* iterators for bishop/rook/queen checks */
  int sq;                               /* var to store pieces at various squares */
  kx = 0;
  ky = 0;
  for (i=1; i <= 8 && kx == 0; i++)
    for (j=1; j <= 8 && ky == 0; j++)
      if
      ((color == WHITE && getsq(board,i,j) == WHITE_KING) ||
       (color == BLACK && getsq(board,i,j) == BLACK_KING) )
      {
        kx = i;
        ky = j;
      }
  if (color == WHITE)
  {
    op = BLACK_PAWN;
    on = BLACK_KNIGHT;
    ob = BLACK_BISHOP;
    or = BLACK_ROOK;
    oq = BLACK_QUEEN;
    ok = BLACK_KING;
  }
  else
  {
    op = WHITE_PAWN;
    on = WHITE_KNIGHT;
    ob = WHITE_BISHOP;
    or = WHITE_ROOK;
    oq = WHITE_QUEEN;
    ok = WHITE_KING;
  }
  if                                    /* check by pawn? */
  (getsq(board,kx-1,color==WHITE?ky+1:ky-1) == op ||
   getsq(board,kx+1,color==WHITE?ky+1:ky-1) == op )
    return 1;
  if                                    /* check by knight? */
  (getsq(board,kx+2,ky+1) == on || getsq(board,kx+1,ky+2) == on ||
   getsq(board,kx-1,ky+2) == on || getsq(board,kx-2,ky+1) == on ||
   getsq(board,kx-2,ky-1) == on || getsq(board,kx-1,ky-2) == on ||
   getsq(board,kx+1,ky-2) == on || getsq(board,kx+2,ky-1) == on )
    return 1;
  for (dx = -1; dx <= 1; dx += 2)       /* bishop+queen check, lower-left ... */
  {
    for (dy = -1; dy <= 1; dy += 2)     /* ... to upper right diagonals */
    {
      h = kx + dx;
      k = ky + dy;
      while (h >= 1 && h <= 8 && k >= 1 && k <= 8)
      {
        sq = getsq(board,h,k);
        if (sq == ob || sq == oq)       /* found opposing bishop or queen along diagonal */
          return 1;
        if (sq != NOTHING)              /* found something else (so not in check) */
          break;
        h += dx;
        k += dy;
      }
    }
  }
  for (i=0; i <= 1; i++)                /* rook+queen check */
  {
    for (j=0; j <= 1; j++)
    {
      if      (i == 0 && j == 0) { dx = -1; dy = 0;  } /* going left, */
      else if (i == 0 && j == 1) { dx = 1;  dy = 0;  } /* right, etc... */
      else if (i == 1 && j == 0) { dx = 0;  dy = -1; }
      else if (i == 1 && j == 1) { dx = 0;  dy = 1;  }
      h = kx + dx;
      k = ky + dy;
      while (h >= 1 && h <= 8 && k >= 1 && k <= 8)
      {
        sq = getsq(board,h,k);
        if (sq == or || sq == oq)       /* found opposing rook or queen along diagonal */
          return 1;
        if (sq != NOTHING)              /* found something else (so not in check) */
          break;
        h += dx;
        k += dy;
      }
    }
  }
  if                                    /* check by king? */
  (getsq(board,kx+1,ky) == ok || getsq(board,kx+1,ky+1) == ok ||
   getsq(board,kx,ky+1) == ok || getsq(board,kx-1,ky+1) == ok ||
   getsq(board,kx-1,ky) == ok || getsq(board,kx-1,ky-1) == ok ||
   getsq(board,kx,ky-1) == ok || getsq(board,kx+1,ky-1) == ok )
    return 1;
  return 0;
}

/* check to see if a game is over */
int isgameover (chessboard board, int color)
{
  /* possible return codes:
   * 0 (game is not over yet)
   * OVER_CHECKMATE
   * OVER_STALEMATE
   * OVER_LACKMATERIAL
   */
  int i, j, u, v;                       /* iterators */
  int dx=0, dy=0, h, k;                 /* iterators for bishop/rook/queen checks */
  int sq;                               /* piece at a square */
  int mov;                              /* legal moves found? */
  int prq,wnb,bnb;                      /* counters for: pawn+rook+queen,white knight+bishop,black knight+bishop */
  mov = 0;
  for (i=1; i <= 8 && mov == 0; i++)
  {
    for (j=1; j <= 8 && mov == 0; j++)
    {
      sq = getsq(board,i,j);
      if ((color == WHITE && sq <= NOTHING) || (color == BLACK && sq >= NOTHING))
        continue;
      switch (sq)
      {
      case WHITE_PAWN:                  /* check for legal pawn moves */
        if      (islegal(board,color,i,j,i,j+1))   mov++;
        else if (islegal(board,color,i,j,i,j+2))   mov++;
        else if (islegal(board,color,i,j,i-1,j+1)) mov++;
        else if (islegal(board,color,i,j,i+1,j+1)) mov++;
        break;
      case BLACK_PAWN:
        if      (islegal(board,color,i,j,i,j-1))   mov++;
        else if (islegal(board,color,i,j,i,j-2))   mov++;
        else if (islegal(board,color,i,j,i-1,j-1)) mov++;
        else if (islegal(board,color,i,j,i+1,j-1)) mov++;
        break;
      case WHITE_KNIGHT:                /* check for legal knight moves */
      case BLACK_KNIGHT:
        if      (islegal(board,color,i,j,i+2,j+1)) mov++;
        else if (islegal(board,color,i,j,i+1,j+2)) mov++;
        else if (islegal(board,color,i,j,i-1,j+2)) mov++;
        else if (islegal(board,color,i,j,i-2,j+1)) mov++;
        else if (islegal(board,color,i,j,i-2,j-1)) mov++;
        else if (islegal(board,color,i,j,i-1,j-2)) mov++;
        else if (islegal(board,color,i,j,i+1,j-2)) mov++;
        else if (islegal(board,color,i,j,i+2,j-1)) mov++;
        break;
      case WHITE_QUEEN:                 /* here again we encapsulate queen check in bishop+rook checks */
      case BLACK_QUEEN:
      case WHITE_BISHOP:                /* check for legal bishop moves */
      case BLACK_BISHOP:
        for (dx = -1; dx <= 1 && mov == 0; dx += 2)
        {
          for (dy = -1; dy <= 1 && mov == 0; dy += 2)
          {
            h = i + dx;
            k = j + dy;
            while (mov == 0 && h >= 1 && h <= 8 && k >= 1 && k <= 8)
            {
              if (islegal(board,color,i,j,h,k)) mov++;
              h += dx;
              k += dy;
            }
          }
        }
        if (sq != WHITE_QUEEN && sq != BLACK_QUEEN)
          break;
      case WHITE_ROOK:                  /* check for legal rook moves */
      case BLACK_ROOK:
        for (u=0; u <= 1 && mov == 0; u++)
        {
          for (v=0; v <= 1 && mov == 0; v++)
          {
            if      (u == 0 && v == 0) { dx = -1; dy = 0;  }
            else if (u == 0 && v == 1) { dx = 1;  dy = 0;  }
            else if (u == 1 && v == 0) { dx = 0;  dy = -1; }
            else if (u == 1 && v == 1) { dx = 0;  dy = 1;  }
            h = i + dx;
            k = j + dy;
            while (mov == 0 && h >= 1 && h <= 8 && k >= 1 && k <= 8)
            {
              if (islegal(board,color,i,j,h,k)) mov++;
              h += dx;
              k += dy;
            }
          }
        }
        break;
      case WHITE_KING:                  /* check for legal king moves */
      case BLACK_KING:
        if      (islegal(board,color,i,j,i+1,j))   mov++;
        else if (islegal(board,color,i,j,i+1,j+1)) mov++;
        else if (islegal(board,color,i,j,i,j+1))   mov++;
        else if (islegal(board,color,i,j,i-1,j+1)) mov++;
        else if (islegal(board,color,i,j,i-1,j))   mov++;
        else if (islegal(board,color,i,j,i-1,j-1)) mov++;
        else if (islegal(board,color,i,j,i,j-1))   mov++;
        else if (islegal(board,color,i,j,i+1,j-1)) mov++;
        /* note: no need to check for castling legality since it wont be legal if none of the above are legal */
        break;
      }
    }
  }
  if (mov == 0)                         /* no moves found... */
  {
    if (ischeck(board,color))           /* position is checkmate */
    {
      return OVER_CHECKMATE;
    }
    else                                /* position is drawn by stalemate */
    {
      return OVER_STALEMATE;
    }
  }
  prq = 0;                              /* otherwise, now we check for lack of material draw */
  wnb = 0;
  bnb = 0;
  for (i=1; i <= 8; i++)
    for (j=1; j<= 8; j++)
      switch (getsq(board,i,j))         /* count each group of pieces */
      {
      case WHITE_PAWN:
      case BLACK_PAWN:
      case WHITE_ROOK:
      case BLACK_ROOK:
      case WHITE_QUEEN:
      case BLACK_QUEEN:
        prq++;
        break;
      case WHITE_KNIGHT:
      case WHITE_BISHOP:
        wnb++;
        break;
      case BLACK_KNIGHT:
      case BLACK_BISHOP:
        bnb++;
        break;
      case WHITE_KING:
      case BLACK_KING:
      case NOTHING:
        break;
      }
  if (prq == 0 && wnb == 1 && bnb == 1)
  {
    /* for later -- add code to allow for the bizarre hypothetical situations
     * where there's a king and minor piece against a king and minor piece but
     * there could still be a win...
     */
    return OVER_LACKMATERIAL;
  }
  else if (prq == 0 && wnb <= 1 && bnb <= 1)
    return OVER_LACKMATERIAL;
  return 0;                             /* made it this far? must not be over */
}

/* generate a move list */
int getmovelist (movetree *moves)
{                   /* return value for this function = number of legal moves for the position */
  int i, j, u, v;                       /* iterators */
  int dx=0, dy=0, h, k;                 /* iterators for bishop/rook/queen checks */
  int sq;                               /* piece at a square */
  int mov;                              /* number of legal moves found */
  movetree *curmoves;                   /* pointer to moves tree */
  if (isgameover(moves->position,moves->position.turn))
    return 0;
  mov = 0;
  curmoves = moves;
  for (i=1; i <= 8; i++)
  {
    for (j=1; j <= 8; j++)
    {
      sq = getsq(moves->position,i,j);
      if
      ((moves->position.turn == WHITE && sq <= NOTHING) ||
       (moves->position.turn == BLACK && sq >= NOTHING) )
        continue;
      switch (sq)
      {
      case WHITE_PAWN:                  /* check for legal pawn moves */
        if (islegal(moves->position,moves->position.turn,i,j,i,j+1))
        {
          if (j == 7)                   /* also add promotions to list */
          {
            curmoves = addmovetolist(curmoves,mov++,moves->position,i,j,i,j+1,WHITE_QUEEN);
            curmoves = addmovetolist(curmoves,mov++,moves->position,i,j,i,j+1,WHITE_KNIGHT);
            curmoves = addmovetolist(curmoves,mov++,moves->position,i,j,i,j+1,WHITE_ROOK);
            curmoves = addmovetolist(curmoves,mov++,moves->position,i,j,i,j+1,WHITE_BISHOP);
          }
          else
            curmoves = addmovetolist(curmoves,mov++,moves->position,i,j,i,j+1,NOTHING);
        }
        if (islegal(moves->position,moves->position.turn,i,j,i,j+2))
          curmoves = addmovetolist(curmoves,mov++,moves->position,i,j,i,j+2,NOTHING);
        if (islegal(moves->position,moves->position.turn,i,j,i-1,j+1))
        {
          if (j == 7)                   /* add promotions to list */
          {
            curmoves = addmovetolist(curmoves,mov++,moves->position,i,j,i-1,j+1,WHITE_QUEEN);
            curmoves = addmovetolist(curmoves,mov++,moves->position,i,j,i-1,j+1,WHITE_KNIGHT);
            curmoves = addmovetolist(curmoves,mov++,moves->position,i,j,i-1,j+1,WHITE_ROOK);
            curmoves = addmovetolist(curmoves,mov++,moves->position,i,j,i-1,j+1,WHITE_BISHOP);
          }
          else
            curmoves = addmovetolist(curmoves,mov++,moves->position,i,j,i-1,j+1,NOTHING);
        }
        if (islegal(moves->position,moves->position.turn,i,j,i+1,j+1))
        {
          if (j == 7)                   /* add promotions to list */
          {
            curmoves = addmovetolist(curmoves,mov++,moves->position,i,j,i+1,j+1,WHITE_QUEEN);
            curmoves = addmovetolist(curmoves,mov++,moves->position,i,j,i+1,j+1,WHITE_KNIGHT);
            curmoves = addmovetolist(curmoves,mov++,moves->position,i,j,i+1,j+1,WHITE_ROOK);
            curmoves = addmovetolist(curmoves,mov++,moves->position,i,j,i+1,j+1,WHITE_BISHOP);
          }
          else
            curmoves = addmovetolist(curmoves,mov++,moves->position,i,j,i+1,j+1,NOTHING);
        }
        break;
      case BLACK_PAWN:
        if (islegal(moves->position,moves->position.turn,i,j,i,j-1))
        {
          if (j == 2)                   /* also add promotions to list */
          {
            curmoves = addmovetolist(curmoves,mov++,moves->position,i,j,i,j-1,BLACK_QUEEN);
            curmoves = addmovetolist(curmoves,mov++,moves->position,i,j,i,j-1,BLACK_KNIGHT);
            curmoves = addmovetolist(curmoves,mov++,moves->position,i,j,i,j-1,BLACK_ROOK);
            curmoves = addmovetolist(curmoves,mov++,moves->position,i,j,i,j-1,BLACK_BISHOP);
          }
          else
            curmoves = addmovetolist(curmoves,mov++,moves->position,i,j,i,j-1,NOTHING);
        }
        if (islegal(moves->position,moves->position.turn,i,j,i,j-2))
          curmoves = addmovetolist(curmoves,mov++,moves->position,i,j,i,j-2,NOTHING);
        if (islegal(moves->position,moves->position.turn,i,j,i-1,j-1))
        {
          if (j == 2)                   /* add promotions to list */
          {
            curmoves = addmovetolist(curmoves,mov++,moves->position,i,j,i-1,j-1,BLACK_QUEEN);
            curmoves = addmovetolist(curmoves,mov++,moves->position,i,j,i-1,j-1,BLACK_KNIGHT);
            curmoves = addmovetolist(curmoves,mov++,moves->position,i,j,i-1,j-1,BLACK_ROOK);
            curmoves = addmovetolist(curmoves,mov++,moves->position,i,j,i-1,j-1,BLACK_BISHOP);
          }
          else
            curmoves = addmovetolist(curmoves,mov++,moves->position,i,j,i-1,j-1,NOTHING);
        }
        if (islegal(moves->position,moves->position.turn,i,j,i+1,j-1))
        {
          if (j == 2)                   /* add promotions to list */
          {
            curmoves = addmovetolist(curmoves,mov++,moves->position,i,j,i+1,j-1,BLACK_QUEEN);
            curmoves = addmovetolist(curmoves,mov++,moves->position,i,j,i+1,j-1,BLACK_KNIGHT);
            curmoves = addmovetolist(curmoves,mov++,moves->position,i,j,i+1,j-1,BLACK_ROOK);
            curmoves = addmovetolist(curmoves,mov++,moves->position,i,j,i+1,j-1,BLACK_BISHOP);
          }
          else
            curmoves = addmovetolist(curmoves,mov++,moves->position,i,j,i+1,j-1,NOTHING);
        }
        break;
      case WHITE_KNIGHT:                /* check for legal knight moves */
      case BLACK_KNIGHT:
        if (islegal(moves->position,moves->position.turn,i,j,i+2,j+1))
          curmoves = addmovetolist(curmoves,mov++,moves->position,i,j,i+2,j+1,NOTHING);
        if (islegal(moves->position,moves->position.turn,i,j,i+1,j+2))
          curmoves = addmovetolist(curmoves,mov++,moves->position,i,j,i+1,j+2,NOTHING);
        if (islegal(moves->position,moves->position.turn,i,j,i-1,j+2))
          curmoves = addmovetolist(curmoves,mov++,moves->position,i,j,i-1,j+2,NOTHING);
        if (islegal(moves->position,moves->position.turn,i,j,i-2,j+1))
          curmoves = addmovetolist(curmoves,mov++,moves->position,i,j,i-2,j+1,NOTHING);
        if (islegal(moves->position,moves->position.turn,i,j,i-2,j-1))
          curmoves = addmovetolist(curmoves,mov++,moves->position,i,j,i-2,j-1,NOTHING);
        if (islegal(moves->position,moves->position.turn,i,j,i-1,j-2))
          curmoves = addmovetolist(curmoves,mov++,moves->position,i,j,i-1,j-2,NOTHING);
        if (islegal(moves->position,moves->position.turn,i,j,i+1,j-2))
          curmoves = addmovetolist(curmoves,mov++,moves->position,i,j,i+1,j-2,NOTHING);
        if (islegal(moves->position,moves->position.turn,i,j,i+2,j-1))
          curmoves = addmovetolist(curmoves,mov++,moves->position,i,j,i+2,j-1,NOTHING);
        break;
      case WHITE_QUEEN:                 /* here again we encapsulate queen check in bishop+rook checks */
      case BLACK_QUEEN:
      case WHITE_BISHOP:                /* check for legal bishop moves */
      case BLACK_BISHOP:
        for (dx = -1; dx <= 1; dx += 2)
        {
          for (dy = -1; dy <= 1; dy += 2)
          {
            h = i + dx;
            k = j + dy;
            while (h >= 1 && h <= 8 && k >= 1 && k <= 8)
            {
              if (islegal(moves->position,moves->position.turn,i,j,h,k))
                curmoves = addmovetolist(curmoves,mov++,moves->position,i,j,h,k,NOTHING);
              h += dx;
              k += dy;
            }
          }
        }
        if (sq != WHITE_QUEEN && sq != BLACK_QUEEN)
          break;
      case WHITE_ROOK:                  /* check for legal rook moves */
      case BLACK_ROOK:
        for (u=0; u <= 1; u++)
        {
          for (v=0; v <= 1; v++)
          {
            if      (u == 0 && v == 0) { dx = -1; dy = 0;  }
            else if (u == 0 && v == 1) { dx = 1;  dy = 0;  }
            else if (u == 1 && v == 0) { dx = 0;  dy = -1; }
            else if (u == 1 && v == 1) { dx = 0;  dy = 1;  }
            h = i + dx;
            k = j + dy;
            while (h >= 1 && h <= 8 && k >= 1 && k <= 8)
            {
              if (islegal(moves->position,moves->position.turn,i,j,h,k))
                curmoves = addmovetolist(curmoves,mov++,moves->position,i,j,h,k,NOTHING);
              h += dx;
              k += dy;
            }
          }
        }
        break;
      case WHITE_KING:                  /* check for legal king moves */
      case BLACK_KING:
        if (islegal(moves->position,moves->position.turn,i,j,i+1,j))
          curmoves = addmovetolist(curmoves,mov++,moves->position,i,j,i+1,j,NOTHING);
        if (islegal(moves->position,moves->position.turn,i,j,i+1,j+1))
          curmoves = addmovetolist(curmoves,mov++,moves->position,i,j,i+1,j+1,NOTHING);
        if (islegal(moves->position,moves->position.turn,i,j,i,j+1))
          curmoves = addmovetolist(curmoves,mov++,moves->position,i,j,i,j+1,NOTHING);
        if (islegal(moves->position,moves->position.turn,i,j,i-1,j+1))
          curmoves = addmovetolist(curmoves,mov++,moves->position,i,j,i-1,j+1,NOTHING);
        if (islegal(moves->position,moves->position.turn,i,j,i-1,j))
          curmoves = addmovetolist(curmoves,mov++,moves->position,i,j,i-1,j,NOTHING);
        if (islegal(moves->position,moves->position.turn,i,j,i-1,j-1))
          curmoves = addmovetolist(curmoves,mov++,moves->position,i,j,i-1,j-1,NOTHING);
        if (islegal(moves->position,moves->position.turn,i,j,i,j-1))
          curmoves = addmovetolist(curmoves,mov++,moves->position,i,j,i,j-1,NOTHING);
        if (islegal(moves->position,moves->position.turn,i,j,i+1,j-1))
          curmoves = addmovetolist(curmoves,mov++,moves->position,i,j,i+1,j-1,NOTHING);
        if (islegal(moves->position,moves->position.turn,i,j,i-2,j))
          curmoves = addmovetolist(curmoves,mov++,moves->position,i,j,i-2,j,NOTHING);
        if (islegal(moves->position,moves->position.turn,i,j,i+2,j))
          curmoves = addmovetolist(curmoves,mov++,moves->position,i,j,i+2,j,NOTHING);
        break;
      }
    }
  }
  return mov;
}

/* add a move to a move list */
movetree *addmovetolist (movetree *moves, int num, chessboard board, int x1, int y1, int x2, int y2, int promote)
{
  movetree *newmoves;
  if (num <= 0)
  {
    moves->down = malloc(sizeof(movetree));
    newmoves = moves->down;
  }
  else
  {
    moves->next = malloc(sizeof(movetree));
    newmoves = moves->next;
  }
  resetmoves(newmoves);
  newmoves->x1 = x1;
  newmoves->y1 = y1;
  newmoves->x2 = x2;
  newmoves->y2 = y2;
  newmoves->promote = promote;
  newmoves->position = board;
  domove(&newmoves->position,x1,y1,x2,y2,promote);
  return newmoves;
}

/* end */
