// Copyright 1996, 1997 by Jon Dart.  All Rights Reserved.

// Console version of Arasan, works with WinBoard

#include "stdafx.h"
#include "board.h"
#include "movegen.h"
#include "search.h"
#include "options.h"
#include "movearr.h"
#include "notation.h"
#include "timectrl.h"
#include "rmove.h"
#include "scoring.h"
#include "chessio.h"
#include "bitmap.h"
#include "bearing.h"
#include "globals.h"
#include "log.h"
#include "util.h"
#include "calctime.h"
extern "C"
{
#include <string.h>
#include <ctype.h>
};
#include "book.h"
#include <fstream.h>
#include <iostream.h>
#include <strstrea.h>
#include <ctype.h>
#include <time.h>
#include <signal.h>
#include <sys/timeb.h>

#define LOG_FILE "games.pgn"

static BOOL verbose = FALSE;
static Search *searcher = NULL;

static ReversibleMove last_move;
static char last_move_image[80];
static char log_file_name[255];
static int32 time_limit = -1;
static int32 time_left = 0;
static int32 opp_time = 0;
static int book_moves = 0;
static int minutes = 5;
static int incr = 0;
static BOOL computer_plays_white = FALSE;
static char *opponent_name = NULL;
static BOOL ics = FALSE;
static BOOL forceMode = FALSE;
static int moves = 40; /* moves in a time control bloc */
static HANDLE searchThread = NULL;
static Board *ponder_board = NULL, *main_board = NULL;
static BOOL ponder_move_ok = FALSE;
static Move predicted_move;
static int terminating = 0;
static int pondering = 0;
static Move ponder_move;
static Search::Statistics stats, last_stats;
static int time_target = 0;
static int num_pending = 0;
static int computer_rating = 0;
static int opponent_rating = 0;
#define MAX_PENDING 10
// stack of pending commands
static char *pending[MAX_PENDING];
static BOOL trace = FALSE;
static BOOL easy = FALSE;

static Time_Info ti;

// forward declaration
static BOOL do_command(char *buf, Board &board);

static BOOL is_move(char *buf)
{
   if (isalpha(*buf) && isdigit(buf[1]) &&
       isalpha(buf[2]) && isdigit(buf[3]))
       return TRUE;
   else if (strcmp(buf,"o-o")==0 || strcmp(buf,"O-O")==0 ||
      strcmp(buf,"o-o-o")==0 || strcmp(buf,"O-O-O") ==0)
       return TRUE;
   else
       return FALSE;
}

static ReversibleMove text_to_move(const Board &board,char *buf)
{
          ReversibleMove rmove;
          if (strcmp(buf,"o-o") == 0 ||
              strcmp(buf,"o-o-o") == 0 ||
              strcmp(buf,"O-O") == 0 ||
              strcmp(buf,"O-O-O") == 0)
          {
              char *p =  buf;
              while (*p) { *p = toupper(*p); p++; }
              ExtendedMove emove(board,Move::Value(buf,board.Side()));
              rmove = ReversibleMove(board,emove);
          }
          else
          {
              Square start = Square::Value(buf);
              Square dest = Square::Value(buf+2);
              if (buf[4])
              {
                  Piece::PieceType promotion = 
                      Piece::CharValue(toupper(buf[4]));
                  rmove = ReversibleMove(board,start,dest,
                    promotion);
              }
              else
                  rmove = ReversibleMove(board,start,dest);
          }
          return rmove;
}

static void save_game()
{
   if (game_moves->num_moves() == 0)
       return;
   ofstream ofs(log_file_name,ios::out | ios::app);
   if (ofs.good())
   {
        ChessIO::store_pgn(ofs, *theLog,
                      computer_plays_white ? White : Black,
                      opponent_name);
        ofs.close();
   }          
   else
   {
        cerr << "error opening logfile" << endl;
        MessageBox(NULL,"error opening log","",MB_OK);
   }
}

// handle ^C interrupts (sent by winboard)
static void __cdecl sigHandler(int)
{
      cout << "got ^C" << endl;
      if (searcher)
      {
          searcher->terminate_now();
      }
      signal(SIGINT,sigHandler);
}

// handle termination
static void __cdecl exitHandler(int)
{
     save_game();
     exit(0);
}

static void move_image(const Board &board, Move m, char *buf)
{
    // Produce a text string representing the move, in a form
    // Xboard understands.
    ExtendedMove emove(board,m);
    if (emove.Special() == ExtendedMove::KCastle)
    {
        strcpy(buf,"o-o");
    }
    else if (emove.Special() == ExtendedMove::QCastle)
    {
        strcpy(buf,"o-o-o");
    }
    else
    {
        *buf++ = m.StartSquare().FileImage();
        *buf++ = m.StartSquare().RankImage();
        *buf++ = m.DestSquare().FileImage();
        *buf++ = m.DestSquare().RankImage();
        if (emove.Special() == ExtendedMove::Promotion)
        {
            // N.b. ICS requires lower case.
            *buf++ = tolower(Piece::Image(m.PromoteTo()));
        }
        *buf = '\0';
    }                  
}

static void post_output(const Search::Statistics &stats)
{
   if (verbose)
   {
      // "post" output for Winboard
      printf("%2d%c %6d %6d %8d %s\n",
             stats.depth,' ',
             (100*stats.value)/64,
             stats.elapsed_time,
             stats.num_nodes,
             stats.best_line_image);
      fflush(stdout);
   }
}

// handle editing the chessboard
static void edit_board(istream &fin, Board &board)
{
  for (int i = 0; i < 64; i++)
     board.Set_Contents(Piece::EmptyPiece(),i);
  static char buf[80];
  static const char pieces[]="PNBRQKpnbrqk";
  ColorType side = White;
  while (fin.getline(buf,80))
  {
    if (strcmp(buf,"white") == 0)
         side = White;
    else if (strcmp(buf,"black")==0)
         side = Black;
    else if (*buf == '#') 
    {
      for (int i = 0; i < 64; i++)
         board.Set_Contents(Piece::EmptyPiece(),i);
    }
    else if (strcmp(buf,"c")==0) 
         side = OppositeColor(side);
    else if (strcmp(buf,".")==0) 
         break;
//    else if (!strcmp(buf,"d")) {
//      DisplayChessBoard(stdout,search);
//    }
    else 
    {
      if (strchr(pieces,buf[0])) {
        char c = tolower(buf[0]);
        Piece p;
        switch (c)
        {
        case 'p':
            p = Piece(Piece::Pawn,side);
            break;
        case 'n':
            p = Piece(Piece::Knight,side);
            break;
        case 'b':
            p = Piece(Piece::Bishop,side);
            break;
        case 'r':
            p = Piece(Piece::Rook,side);
            break;
        case 'q':
            p = Piece(Piece::Queen,side);
            break;
        case 'k':
            p = Piece(Piece::King,side);
            break;
        default:
            break;
        }

        if (p != Piece::InvalidPiece())
        {
           Square sq = Square::Value(buf+1);
           if (!sq.IsInvalid())
              board.Set_Contents(p,sq);
        }
      }
    }
  }
  board.set_secondary_vars();
  // edit doesn't set the castle status, so try to deduce it 
  // from the piece positions
  if (board.KingPos(White) == Square(60))
  {
     if (board[56] == Piece(Piece::Rook,White) &&
         board[63] == Piece(Piece::Rook,White))
        board.Set_CastleStatus(Board::CanCastleEitherSide,White);
     else if (board[56] == Piece(Piece::Rook,White))
        board.Set_CastleStatus(Board::CanCastleQSide,White);
     else if (board[63] == Piece(Piece::Rook,White))
        board.Set_CastleStatus(Board::CanCastleKSide,White);
  }
  else
    board.Set_CastleStatus(Board::CantCastleEitherSide,White);
  if (board.KingPos(Black) == Square(4))
  {
     if (board[0] == Piece(Piece::Rook,Black) &&
         board[7] == Piece(Piece::Rook,Black))
        board.Set_CastleStatus(Board::CanCastleEitherSide,Black);
     else if (board[0] == Piece(Piece::Rook,Black))
        board.Set_CastleStatus(Board::CanCastleQSide,Black);
     else if (board[7] == Piece(Piece::Rook,Black))
        board.Set_CastleStatus(Board::CanCastleKSide,Black);
  }
  else
    board.Set_CastleStatus(Board::CantCastleEitherSide,Black);
}  

static void ponder(Board &board, Move move, Move predicted_reply)
{
      ponder_move_ok = FALSE;
      ponder_move.MakeNull();
      if (!move.IsNull() && !predicted_reply.IsNull() && 
          !terminating)
      {
          // start pondering
          predicted_move = predicted_reply;
          if (ponder_board) delete ponder_board;
          ponder_board = new Board(board);
          // Make the move we just calculated
          //ponder_board->MakeMove(rmove);
          // Make the move we predicted
          ExtendedMove emove = ExtendedMove(*ponder_board,predicted_reply);
          ReversibleMove rmove = ReversibleMove(*ponder_board,emove);
          ponder_board->MakeMove(rmove);
          // Start a search for the reply
          stats.clear();
          ponder_move = opening_book->book_move(board,Balanced);
          if (ponder_move.IsNull())
          {
              pondering++;
              if (trace)
                 cout << "(debug) starting to ponder" << endl;
              ponder_move =
                  searcher->find_best_move(NULL,*ponder_board,
                  Time_Target,
                  99999 /* time limit (999 seconds) */,
                  99 /* ply limit */,
                  TRUE /* background */,
                  stats,
                  (trace) ? Search::Trace : Search::Silent);
              pondering--;
              if (trace)
                 cout << "(debug) done pondering" << endl;
          }
      }
}

static Move search(Search &searcher, Board &board, Search::Statistics &stats)
{
    Move pred;
    last_stats.clear();
    Move move = opening_book->book_move(board,Balanced);
    if (move.IsNull())
    {
        // no book move
        time_target = calc_time_limit(moves, minutes,incr,time_left,opp_time);
        if (ics && time_target >= 100)
           cout << "whisper time target = " << time_target/100 << " seconds " <<
           endl;

        Search_Limit limit;
        limit.centiseconds = time_target;
//            cout << "time target = " << limit.centiseconds << endl;
        stats.clear(); 
        if (trace)
          cout << "(debug) entering search" << endl;
        move = searcher.find_best_move(NULL, board, 
             Time_Target, time_target, 9999, FALSE,
             stats, 
//                ics ? Search::Silent : Search::Whisper);
                Search::Whisper);
        if (trace)
           cout << "(debug) search done " << endl;
        last_stats = stats;
        post_output(stats);
    }
    else
    {
        cout << "whisper book move" << endl;
        book_moves++;
    }
    return move;
}

static void send_move(Board &board, Move &move, Search::Statistics
&stats)
{
    ExtendedMove emove(board,move);
    last_move = ReversibleMove(board,emove);
    last_stats = stats;
    if (!emove.IsNull())
    {
        Notation::Image(board,last_move,last_move_image);
        theLog->add_move(board,last_move,last_move_image,&last_stats,TRUE);
        char movebuf[20];
        move_image(board,last_move,movebuf);
        cout << 1+game_moves->num_moves()/2 << ". ... ";
        cout << movebuf << endl;
        // for Gnuchess compatibility
        cout << "My move is: " << movebuf << endl;
        board.MakeMove(ReversibleMove(board,last_move));
        game_moves->add_move(board,last_move);
    }
    else
        cout << "(debug) warning : move is null" << endl;
    if (ics && time_target >= 100)
    {
       char val[20];
       sprintf(val,"%6.2f",last_stats.value/64.0);
       static char statbuf[80];
       
       if (last_stats.num_moves > 0)
       {
          sprintf(statbuf,"time : %d.%02d seconds  score: %6.2f  depth: %d  nodes : %d",
              last_stats.elapsed_time/100,
              last_stats.elapsed_time % 100,
              last_stats.value/64.0,
              last_stats.depth,
              last_stats.num_nodes);
           cout << "whisper " << statbuf << endl;
       }
       if (last_stats.best_line_image[0] != '\0')
       {
           char *p = strchr(last_stats.best_line_image,' ');
           if (p && *(p+1) != '\0')
              cout << "whisper predicting: " << p+1 << endl;
       }
    }
	int rep_count;
    if (last_stats.state == Search::Draw ||
          last_stats.state == Search::Stalemate || 
        Search::is_draw(board,rep_count,0))
    {
        // ICS requires that we claim a draw by repetition.  Winboard
        // also expects to be notified.
        cout << "Draw" << endl;
        theLog->setResult("1/2-1/2");
    }
    else if (last_stats.value >= 9999)
    {
        if (computer_plays_white)
            theLog->setResult("1-0");
        else
            theLog->setResult("0-1");
        if (!ics)
        {
           if (computer_plays_white)
               cout << "White mates!" << endl;
           else
               cout << "Black mates!" << endl;
        }
    }
    else if (last_stats.state == Search::Checkmate)
    {
        if (computer_plays_white)
            theLog->setResult("0-1");
        else
            theLog->setResult("1-0");
        if (!ics)
        {
           if (computer_plays_white)
               cout << "White mates!" << endl;
           else
               cout << "Black mates!" << endl;
        }
    }
    else if (last_stats.state == Search::Resigns)
    {
        // Don't resign a zero-increment game if the opponent is short
        // on time
        if (!(incr == 0 && opp_time < 2000))
        {
           // Winboard passes the resign command to ICS, but ignores it
           // itself.
           cout << "resign" << endl;
           if (computer_plays_white)
               theLog->setResult("0-1");
           else
               theLog->setResult("1-0");
        }
    }
}    

static void undo( Board &board)
{
         board.UndoMove((*theLog)[theLog->current()-1].move());
         theLog->back_up();
         game_moves->remove_move();
         if (theLog->current())
         {
             last_move = (*theLog)[theLog->current()-1].move();
             strcpy(last_move_image,(*theLog)[theLog->current()-1].image());
         }
         else
         {
             last_move.MakeNull();
             *last_move_image = '\0';
         }
}

static void add_pending(char *buf)
{
   if (num_pending < MAX_PENDING)
   {
      pending[num_pending] = strdup(buf);
      ++num_pending;
   }
}

static BOOL do_pending(Board &board)
{
   while (num_pending)
   {
      --num_pending;
      BOOL terminate;
      if (trace)
        cout << "(debug) pending command: " << pending[num_pending] << endl;
      terminate = !do_command(pending[num_pending],board);
      free( pending[num_pending]);
      if (terminate)
      {
         return FALSE;
      }
   }
   return TRUE;
}

static void execute_move(Board &board,ReversibleMove &rmove)
{
    last_move = rmove;
    Notation::Image(board,rmove,last_move_image);
    theLog->add_move(board,last_move,last_move_image,NULL,TRUE);

    board.MakeMove(rmove);
    game_moves->add_move(board,rmove);
}

// Handle a command received while searching.  terminate is set
// TRUE if the search should stop.  Some commands are executed,
// some are placed on the pending stack to be executed after
// the search completes.
void check_command(char *buf, BOOL &terminate)
{
      if (trace)
         cout << "(debug) check_command: " << buf << endl;
      terminate = FALSE;
      if (strcmp(buf,"quit")==0 || strcmp(buf,"end")==0)
      {
         add_pending(buf);
         terminate = TRUE;
      }
      else if (strcmp(buf,"new")==0)
      {
         add_pending(buf);
         terminate = TRUE;
      }
      else if (strcmp(buf,"?") == 0)
      {
         // Winboard 3.6 sends this to terminate a search in progress
         if (trace) cout << "(debug) terminating." << endl;
         terminate = TRUE;
      }
      else if (strcmp(buf,"hint")==0)
      {
      }
      else if (strcmp(buf,"depth")==0)
      {
      }
      else if (strncmp(buf,"level",5)==0)
      {
         sscanf(buf+6,"%ld %ld %ld",&moves,&minutes,&incr);
      }
      else if (strncmp(buf,"time",4)==0)
      {
          // my time left
          sscanf(buf+4,"%ld",&time_left);
      }
      else if (strncmp(buf,"otime",4)==0)
      {
          // opponent's time left
          sscanf(buf+4,"%ld",&opp_time);
      }
      else if (strcmp(buf,"post")==0)
      { 
         verbose = TRUE;
      }
      else if (strcmp(buf,"nopost")==0)
      {
         verbose = FALSE;
      }
      else if (strcmp(buf,"savegame")==0)
      {
      }
      else if (strcmp(buf,"remove")==0)
      {
          add_pending(buf);
          terminate = TRUE;
      }
      else if (strcmp(buf,"undo")==0)
      {
          add_pending(buf);
          terminate = TRUE;
      }
      else if (strcmp(buf,"resign")==0)
      {
          add_pending(buf);
          terminate = TRUE;
      }
      else if (strcmp(buf,"draw")==0)
      {
          add_pending(buf);
          terminate = TRUE;
      }
      else if (strcmp(buf,"edit")==0)
      {
          add_pending(buf);
          terminate = TRUE;
      }
      else if (strcmp(buf,"go")==0)
      {
          add_pending(buf);
          terminate = TRUE;
      }
      else if (strcmp(buf,"bk")==0)
      {
      }
      else if (strcmp(buf,"easy")==0)
      {
          easy = TRUE;
      }
      else if (strcmp(buf,"hard")==0)
      {
          easy = FALSE;
      }
      else if (strcmp(buf,"white")==0)
      {
          add_pending(buf);
          terminate = TRUE;
      }
      else if (strcmp(buf,"black")==0)
      {
          add_pending(buf);
          terminate = TRUE;
      }
      else if (strncmp(buf,"name",4)==0)
      {
         // We've received the name of our opponent.
         opponent_name = strdup(buf+5);
      }
      else if (strncmp(buf,"bogus",5)==0)
      {
      }
      else if (strncmp(buf,"force",5)==0)
      {
          forceMode = !forceMode;      
          terminate = TRUE;
      }
      else if (strncmp(buf,"rating",6) == 0)
      {
          sscanf(buf+7,"%d %d",&computer_rating, &opponent_rating);
      }
      else
      {
          // see if it could be a move
          if (isdigit(*buf))
          {
             char *p = buf;
             while (*p && !isalpha(*p)) ++p;
             if (*p)
             {
                char *q = buf;
                while (*p) *q++=*p++;
             }
          }
          if (trace)
             cout << "(debug) move text = " << buf << endl;
          if (is_move(buf))
          {
               // make the move on the board
               ReversibleMove rmove = text_to_move(*main_board,buf);
               last_move = rmove;

               if (forceMode)
               {
                   add_pending(buf);
                   terminate = TRUE;
               }
               else if (!predicted_move.IsNull() &&
                   predicted_move == last_move)
               {
                  // make the move on the board
                  execute_move(*main_board,last_move);

                  // We predicted the opponent's move, so we need to
                  // continue doing the search but adjust the time
                  // limit.
                  if (ics && time_target >= 100)
                     cout << "whisper predicted last move" << endl;
                  ponder_move_ok = TRUE;
                  int elapsed_time = (GetTickCount()/10)-searcher->get_start_time();
                  // Search a little less than "time_target" beyond the
                  // current elapsed time
                  int32 new_time;
                  if (elapsed_time > time_target/5)
                    new_time = time_target + elapsed_time - time_target/5;
                  else
                    new_time = time_target + elapsed_time;
                  if (new_time <= 0) new_time = 30;
                  if (trace)
                    cout << "(debug) new search limit = " << new_time <<endl;
                  searcher->set_time_limit(new_time);
                  searcher->set_talk_level(Search::Whisper);
                  searcher->set_background(FALSE);
                  terminate = FALSE;
               }
               else
               {
                  // We can't use the results of pondering because we
                  // did not predict the opponent's move.  Stop the
                  // search and then execute the move.
                  ponder_move_ok = FALSE;
                  add_pending(buf);
                  terminate = TRUE;
               }
          }
      }
}

// Execute a command, return FALSE if program should terminate.
static BOOL do_command(char *buf, Board &board)
{
      if (trace)
         cout << "(debug) do_command: " << buf << endl;
      if (strcmp(buf,"quit")==0 || strcmp(buf,"end")==0)
      {
         return FALSE;
      }
      else if (strcmp(buf,"new")==0)
      {
         save_game();  
         board.Reset();
         theLog->reset();
         theLog->write_header();
         computer_plays_white = FALSE;
         forceMode = FALSE;
         stats.clear();
         last_stats.clear();
         last_move.MakeNull();
         *last_move_image = '\0';
         delete game_moves;
         game_moves = new Move_Array();
      }
      else if (strcmp(buf,"hint")==0)
      {
      }
      else if (strcmp(buf,"depth")==0)
      {
      }
      else if (strncmp(buf,"level",5)==0)
      {
         sscanf(buf+6,"%ld %ld %ld",&moves,&minutes,&incr);
//         cout << "level command: " << buf;
      }
      else if (strncmp(buf,"time",4)==0)
      {
          // my time left
          sscanf(buf+4,"%ld",&time_left);
      }
      else if (strncmp(buf,"otime",4)==0)
      {
          sscanf(buf+4,"%ld",&opp_time);
      }
      else if (strcmp(buf,"post")==0)
      { 
         verbose = TRUE;
      }
      else if (strcmp(buf,"nopost")==0)
      {
         verbose = FALSE;
      }
      else if (strcmp(buf,"savegame")==0)
      {
      }
      else if (strcmp(buf,"remove")==0)
      {
          undo(board);
          undo(board);
      }
      else if (strcmp(buf,"undo")==0)
      {
          undo(board);
      }
      else if (strcmp(buf,"resign")==0)
      {
        if (computer_plays_white)
            theLog->setResult("1-0");
        else
            theLog->setResult("0-1");
      }
      else if (strcmp(buf,"draw")==0)
      {
         theLog->setResult("1/2-1/2");
      }
      else if (strcmp(buf,"edit")==0)
      {
         edit_board(cin,board);
      }
      else if (strcmp(buf,"go")==0)
      {
         cout << "1. go" << endl;
         Move reply = search(*searcher,board,stats);
         send_move(board,reply,stats);
//         if (time_target >= 300) // 3 seconds
//         {
//            ponder(board, reply, stats.best_line[1]);
//         }
      }
      else if (strcmp(buf,"bk")==0)
      {
      }
      else if (strcmp(buf,"easy")==0)
      {
          easy = TRUE;
      }
      else if (strcmp(buf,"hard")==0)
      {
          easy = FALSE;
      }
      else if (strcmp(buf,"white")==0)
      {
         computer_plays_white = TRUE;
         forceMode = FALSE;
      }
      else if (strcmp(buf,"black")==0)
      {
         computer_plays_white = FALSE;
         forceMode = FALSE;
      }
      else if (strncmp(buf,"name",4)==0)
      {
         // We've received the name of our opponent.
         opponent_name = strdup(buf+5);
      }
      else if (strncmp(buf,"bogus",5)==0)
      {
      }
      else if (strncmp(buf,"force",5)==0)
      {
          forceMode = !forceMode;      
      }
      else if (strncmp(buf,"rating",6) == 0)
      {
          sscanf(buf+7,"%s %d %d",&computer_rating, &opponent_rating);
      }
      else
      {
          // see if it could be a move
          if (isdigit(*buf))
          {
             char *p = buf;
             while (*p && !isalpha(*p)) ++p;
             if (*p)
             {
                char *q = buf;
                while (*p) *q++=*p++;
             }
          }
//          cout << "move text = " << buf << endl;
          if (is_move(buf))
          {
          if (trace)
             cout << "(debug) got move: " << buf << endl;
          // assume move
          cout << game_moves->num_moves() << ". " << buf << endl;

          ReversibleMove rmove = text_to_move(board,buf);
          if (!rmove.IsNull())
          {
             // make the move on the board
             execute_move(board,rmove);
             Move reply;
             if (!forceMode)
             {
                reply = search(*searcher,board,stats);
                send_move(board,reply,stats);
             }
             if (!forceMode && !easy && time_target >= 300 /* 3 seconds */)
             {
                ponder(board, reply, stats.best_line[1]);
                ExtendedMove emove(board,ponder_move);
                ReversibleMove rmove(board,emove);
                if (ponder_move_ok && !ponder_move.IsNull())
                {
                   // we got a reply from pondering
                   if (trace)
                     cout << "(debug) sending ponder move" << endl;
                   send_move(board,rmove,stats);
                   post_output(stats);
                }
                else // no ponder move
                {
                    return TRUE;
                }
             }
          }
          }
      }
      return TRUE;
}

int __cdecl main(int argc, char **argv)
{
   signal(SIGINT, sigHandler);
//   signal(SIGTERM, exitHandler);

   init_globals(argv[0],TRUE,TRUE);
   atexit(cleanup_globals);

   Board board;
   main_board = &board;
   Search_Limit limit;
   Search_Type type = Fixed_Ply;
   limit.max_ply = 2;
   int arg = 1;
   
   if (argc > 1)
   {
      while (arg < argc && *(argv[arg]) == '-')
      {
	   char c = *(argv[arg]+1);
	   switch (c)
	   {
  	   case 'i':
	      if (strcmp(argv[arg]+1,"ics")==0)
                 ics = TRUE;
              else
              {
                 cerr << "Warning: unknown option: " << argv[arg]+1 <<
                 endl;
              }
              break;
           case 'H': 
              ++arg;
              SearchOps ops;
 	      global_options->get_search_ops(ops);
              ops.hash_table_size = atol(argv[arg]);
              global_options->update_search_ops(ops);
              break;      
           case 't':
              ++arg;
              trace = TRUE;
              break;
           default:
              cerr << "Warning: unknown option: " << argv[arg]+1 <<
              endl;
              break;
           }
           ++arg;
      }
   }
   if (arg < argc)
   {
      ifstream pos_file( argv[arg], ios::in | ios::nocreate);
      if (pos_file.good())
      {
	  pos_file >> board;
      }	
      else
      {
          cout << "file not found: " << argv[arg] << endl;
          return -1;
      }
   }
   *last_move_image = '\0';
   
   strcpy(log_file_name,programPath);
   char *p = strrchr(log_file_name,'\\');
   if (p)
      strcpy(p+1,LOG_FILE);
   else 
      strcpy(log_file_name,LOG_FILE);
   
   searcher = new Search();
   searcher->register_command_function(check_command);
   searcher->register_post_function(post_output);
   
   // Show a message on the console
   cout << "Arasan " Arasan_Version << " Copyright 1997 by Jon Dart" << endl;

   // Read stdin until we get a quit command
   setbuf(stdin,0);
   int nextc;
   while ((nextc = getc(stdin)) != EOF)
   {
      if (nextc == '\n')
      {
         cmd_buf[cmd_buf_count] = '\0';
         cmd_buf_count = '\0';
//       cout << "received " << cmd_buf << endl;     
         if (!do_command(cmd_buf,board))
             break;
         if (!do_pending(board))
             break;
      }
      else
      {
         if (cmd_buf_count >= 255)
         {
            cout << "(debug) warning: command buffer full" << endl;
            cmd_buf_count = 0;
         }
         cmd_buf[cmd_buf_count++] = nextc;
      }
   }

   // handle termination.
   if (opp_time <= 0)
   {
        if (computer_plays_white)
            theLog->setResult("1-0");
        else
            theLog->setResult("0-1");
   }
   else if (time_left <= 0)
   {
        if (computer_plays_white)
            theLog->setResult("0-1");
        else
            theLog->setResult("1-0");
   }
   save_game();
   searcher->terminate_now();
   if (searchThread)
     WaitForSingleObject(searchThread,2000);
   delete searcher;
   
   cout << "quit" << endl;
   Sleep(2000);
   return 0;
}
