uses dos;

const VERSION='Protej 0.1.1';
      STARTPOSITION='rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1';
      NO_MOVE=128;
      WHITE=false;
      BLACK=true;
      EMPTY=0;
      WPAWN=1;
      WKNIGHT=2;
      WBISHOP=3;
      WROOK=4;
      WQUEEN=5;
      WKING=6;
      BPAWN=WPAWN+16;
      BKNIGHT=WKNIGHT+16;
      BBISHOP=WBISHOP+16;
      BROOK=WROOK+16;
      BQUEEN=WQUEEN+16;
      BKING=WKING+16;
      
      A1=00;B1=01;C1=02;D1=03;E1=04;F1=05;G1=06;H1=07;
      A2=08;B2=09;C2=10;D2=11;E2=12;F2=13;G2=14;H2=15;
      A3=16;B3=17;C3=18;D3=19;E3=20;F3=21;G3=22;H3=23;
      A4=24;B4=25;C4=26;D4=27;E4=28;F4=29;G4=30;H4=31;
      A5=32;B5=33;C5=34;D5=35;E5=36;F5=37;G5=38;H5=39;
      A6=40;B6=41;C6=42;D6=43;E6=44;F6=45;G6=46;H6=47;
      A7=48;B7=49;C7=50;D7=51;E7=52;F7=53;G7=54;H7=55;
      A8=56;B8=57;C8=58;D8=59;E8=60;F8=61;G8=62;H8=63;

type _list=record
       num:byte;
       san:array[0..255]of string[5]
     end;
     _position=record
       board:array[0..63]of byte;
       wk,wq,bk,bq,whiteMove:boolean;
       ep:byte
     end;

var MOVETABLE_N,MOVETABLE_K,MOVETABLE_B1,MOVETABLE_B2,MOVETABLE_B3,MOVETABLE_B4:array[0..7,0..63]of byte;
    MOVETABLE_R1,MOVETABLE_R2,MOVETABLE_R3,MOVETABLE_R4:array[0..8,0..63]of byte;
    MOVETABLE_WP,MOVETABLE_BP:array[0..3,0..63]of byte;

procedure init_movetable;
var row,col,square,vstep,hstep,index:integer;
begin
  for row:=0 to 7 do for col:=0 to 7 do begin
    square:=row*8+col;
    // King
    index:=0;
    for vstep:=-1 to 1 do for hstep:=-1 to 1 do if(vstep<>0)or(hstep<>0)then begin
      if(row+vstep in[0..7])and(col+hstep in[0..7])then MOVETABLE_K[index,square]:=(row+vstep)*8+col+hstep else MOVETABLE_K[index,square]:=NO_MOVE;
      inc(index)
    end;
    // Knight
    index:=0;
    for vstep:=-2 to 2 do for hstep:=-2 to 2 do if(abs(vstep)<>abs(hstep))and(vstep*hstep<>0)then begin
      if(row+vstep in[0..7])and(col+hstep in[0..7])then MOVETABLE_N[index,square]:=(row+vstep)*8+col+hstep else MOVETABLE_N[index,square]:=NO_MOVE;
      inc(index)
    end;
    // Rook
    for index:=1 to 9 do if col+index<8 then MOVETABLE_R1[index-1,square]:=row*8+col+index else MOVETABLE_R1[index-1,square]:=NO_MOVE;
    for index:=1 to 9 do if col-index>=0 then MOVETABLE_R2[index-1,square]:=row*8+col-index else MOVETABLE_R2[index-1,square]:=NO_MOVE;
    for index:=1 to 9 do if row+index<8 then MOVETABLE_R3[index-1,square]:=(row+index)*8+col else MOVETABLE_R3[index-1,square]:=NO_MOVE;
    for index:=1 to 9 do if row-index>=0 then MOVETABLE_R4[index-1,square]:=(row-index)*8+col else MOVETABLE_R4[index-1,square]:=NO_MOVE;
    // Bishop
    for index:=1 to 8 do if(row+index<8)and(col+index<8)then MOVETABLE_B1[index-1,square]:=(row+index)*8+col+index else MOVETABLE_B1[index-1,square]:=NO_MOVE;
    for index:=1 to 8 do if(row+index<8)and(col-index>=0)then MOVETABLE_B2[index-1,square]:=(row+index)*8+col-index else MOVETABLE_B2[index-1,square]:=NO_MOVE;
    for index:=1 to 8 do if(row-index>=0)and(col+index<8)then MOVETABLE_B3[index-1,square]:=(row-index)*8+col+index else MOVETABLE_B3[index-1,square]:=NO_MOVE;
    for index:=1 to 8 do if(row-index>=0)and(col-index>=0)then MOVETABLE_B4[index-1,square]:=(row-index)*8+col-index else MOVETABLE_B4[index-1,square]:=NO_MOVE;
    if row in[1..6]then begin
      // Pawns index: 0,1=captures 2=normal move 3=double step NO_MOVE=none
      // White Pawn
      if col=0 then MOVETABLE_WP[0,square]:=NO_MOVE else MOVETABLE_WP[0,square]:=square+7;
      if col=7 then MOVETABLE_WP[1,square]:=NO_MOVE else MOVETABLE_WP[1,square]:=square+9;
      if row=1 then MOVETABLE_WP[3,square]:=square+16 else MOVETABLE_WP[3,square]:=NO_MOVE;
      MOVETABLE_WP[2,square]:=square+8;
      // Black Pawn
      if col=0 then MOVETABLE_BP[0,square]:=NO_MOVE else MOVETABLE_BP[0,square]:=square-9;
      if col=7 then MOVETABLE_BP[1,square]:=NO_MOVE else MOVETABLE_BP[1,square]:=square-7;
      if row=6 then MOVETABLE_BP[3,square]:=square-16 else MOVETABLE_BP[3,square]:=NO_MOVE;
      MOVETABLE_BP[2,square]:=square-8
    end
  end
end;

function square_to_string(square:byte):string;
begin
  exit(chr(square and 7+97)+chr(square shr 3+49))
end;

function row_to_char(row:byte):char;
begin
  exit(chr(row shr 3+49))
end;

function col_to_char(col:byte):char;
begin
  exit(chr(col and 7+97))
end;

function set_board(fen:string):_position;
var row,index,col:byte;
    ch:char;
begin
  for index:=0 to 63 do set_board.board[index]:=EMPTY;
  index:=1;
  col:=0;
  row:=7;
  repeat
    ch:=fen[index];
    inc(index);
    case ch of
      'K':begin set_board.board[row*8+col]:=WKING;inc(col)end;
      'k':begin set_board.board[row*8+col]:=BKING;inc(col)end;
      'Q':begin set_board.board[row*8+col]:=WQUEEN;inc(col)end;
      'q':begin set_board.board[row*8+col]:=BQUEEN;inc(col)end;
      'B':begin set_board.board[row*8+col]:=WBISHOP;inc(col)end;
      'b':begin set_board.board[row*8+col]:=BBISHOP;inc(col)end;
      'N':begin set_board.board[row*8+col]:=WKNIGHT;inc(col)end;
      'n':begin set_board.board[row*8+col]:=BKNIGHT;inc(col)end;
      'R':begin set_board.board[row*8+col]:=WROOK;inc(col)end;
      'r':begin set_board.board[row*8+col]:=BROOK;inc(col)end;
      'P':begin set_board.board[row*8+col]:=WPAWN;inc(col)end;
      'p':begin set_board.board[row*8+col]:=BPAWN;inc(col)end;
      '1'..'8':begin inc(col,ord(ch)-48)end;
      '/':begin dec(row);col:=0 end
    end
  until index=pos(' ',fen);
  if fen[index+1]='w'then set_board.whiteMove:=true else set_board.whiteMove:=false;
  fen:=copy(fen,index+3,length(fen)-index-2);
  // TBD castles, ep, ply, 50-move count
  set_board.wk:=true;
  set_board.wq:=true;
  set_board.bk:=true;
  set_board.bq:=true;
  set_board.ep:=NO_MOVE
end;

procedure new_game(var p:_position);
begin
  p:=set_board(STARTPOSITION)
end;

function generate_moves(movepos:_position):_list;
var square,index,dest,moves:byte;

  function is_attacked(var attpos:_position;square:byte;byBlack:boolean):boolean;
  var index,dest,row:byte;
  begin
    if byBlack then begin
      // King attacks
      for index:=0 to 7 do if(MOVETABLE_K[index,square]<NO_MOVE)and(attpos.board[MOVETABLE_K[index,square]]=BKING)then exit(true);
      // Knight attacks
      for index:=0 to 7 do if(MOVETABLE_N[index,square]<NO_MOVE)and(attpos.board[MOVETABLE_N[index,square]]=BKNIGHT)then exit(true);
      // Pawn attacks
      row:=square div 8;
      if row<6 then begin
        dest:=square-row*8;
        case dest of
          0:if attpos.board[square+9]=BPAWN then exit(true);
          1..6:if(attpos.board[square+9]=BPAWN)or(attpos.board[square+7]=BPAWN)then exit(true);
          7:if attpos.board[square+7]=BPAWN then exit(true)
        end
      end;
      // Rook and Queen attacks
      index:=0;
      repeat
        dest:=MOVETABLE_R1[index,square];
        if(dest<NO_MOVE)and(attpos.board[dest]in[BROOK,BQUEEN])then exit(true);
        inc(index)
      until(dest=NO_MOVE)or(attpos.board[dest]>EMPTY);
      index:=0;
      repeat
        dest:=MOVETABLE_R2[index,square];
        if(dest<NO_MOVE)and(attpos.board[dest]in[BROOK,BQUEEN])then exit(true);
        inc(index)
      until(dest=NO_MOVE)or(attpos.board[dest]>EMPTY);
      index:=0;
      repeat
        dest:=MOVETABLE_R3[index,square];
        if(dest<NO_MOVE)and(attpos.board[dest]in[BROOK,BQUEEN])then exit(true);
        inc(index)
      until(dest=NO_MOVE)or(attpos.board[dest]>EMPTY);
      index:=0;
      repeat
        dest:=MOVETABLE_R4[index,square];
        if(dest<NO_MOVE)and(attpos.board[dest]in[BROOK,BQUEEN])then exit(true);
        inc(index)
      until(dest=NO_MOVE)or(attpos.board[dest]>EMPTY);
      // Bishop and Queen attacks
      index:=0;
      repeat
        dest:=MOVETABLE_B1[index,square];
        if(dest<NO_MOVE)and(attpos.board[dest]in[BBISHOP,BQUEEN])then exit(true);
        inc(index)
      until(dest=NO_MOVE)or(attpos.board[dest]>EMPTY);
      index:=0;
      repeat
        dest:=MOVETABLE_B2[index,square];
        if(dest<NO_MOVE)and(attpos.board[dest]in[BBISHOP,BQUEEN])then exit(true);
        inc(index)
      until(dest=NO_MOVE)or(attpos.board[dest]>EMPTY);
      index:=0;
      repeat
        dest:=MOVETABLE_B3[index,square];
        if(dest<NO_MOVE)and(attpos.board[dest]in[BBISHOP,BQUEEN])then exit(true);
        inc(index)
      until(dest=NO_MOVE)or(attpos.board[dest]>EMPTY);
      index:=0;
      repeat
        dest:=MOVETABLE_B4[index,square];
        if(dest<NO_MOVE)and(attpos.board[dest]in[BBISHOP,BQUEEN])then exit(true);
        inc(index)
      until(dest=NO_MOVE)or(attpos.board[dest]>EMPTY)
    end else begin
      // King attacks
      for index:=0 to 7 do if(MOVETABLE_K[index,square]<NO_MOVE)and(attpos.board[MOVETABLE_K[index,square]]=WKING)then exit(true);
      // Knight attacks
      for index:=0 to 7 do if(MOVETABLE_N[index,square]<NO_MOVE)and(attpos.board[MOVETABLE_N[index,square]]=WKNIGHT)then exit(true);
      // Pawn attacks
      row:=square div 8;
      if row>1 then begin
        dest:=square-row*8;
        case dest of
          0:if attpos.board[square-7]=WPAWN then exit(true);
          1..6:if(attpos.board[square-9]=WPAWN)or(attpos.board[square-7]=WPAWN)then exit(true);
          7:if attpos.board[square-9]=WPAWN then exit(true)
        end
      end;
      // Rook and Queen attacks
      index:=0;
      repeat
        dest:=MOVETABLE_R1[index,square];
        if(dest<NO_MOVE)and(attpos.board[dest]in[WROOK,WQUEEN])then exit(true);
        inc(index)
      until(dest=NO_MOVE)or(attpos.board[dest]>EMPTY);
      index:=0;
      repeat
        dest:=MOVETABLE_R2[index,square];
        if(dest<NO_MOVE)and(attpos.board[dest]in[WROOK,WQUEEN])then exit(true);
        inc(index)
      until(dest=NO_MOVE)or(attpos.board[dest]>EMPTY);
      index:=0;
      repeat
        dest:=MOVETABLE_R3[index,square];
        if(dest<NO_MOVE)and(attpos.board[dest]in[WROOK,WQUEEN])then exit(true);
        inc(index)
      until(dest=NO_MOVE)or(attpos.board[dest]>EMPTY);
      index:=0;
      repeat
        dest:=MOVETABLE_R4[index,square];
        if(dest<NO_MOVE)and(attpos.board[dest]in[WROOK,WQUEEN])then exit(true);
        inc(index)
      until(dest=NO_MOVE)or(attpos.board[dest]>EMPTY);
      // Bishop and Queen attacks
      index:=0;
      repeat
        dest:=MOVETABLE_B1[index,square];
        if(dest<NO_MOVE)and(attpos.board[dest]in[WBISHOP,WQUEEN])then exit(true);
        inc(index)
      until(dest=NO_MOVE)or(attpos.board[dest]>EMPTY);
      index:=0;
      repeat
        dest:=MOVETABLE_B2[index,square];
        if(dest<NO_MOVE)and(attpos.board[dest]in[WBISHOP,WQUEEN])then exit(true);
        inc(index)
      until(dest=NO_MOVE)or(attpos.board[dest]>EMPTY);
      index:=0;
      repeat
        dest:=MOVETABLE_B3[index,square];
        if(dest<NO_MOVE)and(attpos.board[dest]in[WBISHOP,WQUEEN])then exit(true);
        inc(index)
      until(dest=NO_MOVE)or(attpos.board[dest]>EMPTY);
      index:=0;
      repeat
        dest:=MOVETABLE_B4[index,square];
        if(dest<NO_MOVE)and(attpos.board[dest]in[WBISHOP,WQUEEN])then exit(true);
        inc(index)
      until(dest=NO_MOVE)or(attpos.board[dest]>EMPTY)
    end;
    exit(false)
  end;

  function is_legal(var legalBoard:_position):boolean;
  var sq:byte;
  begin
    if movepos.whiteMove then begin
      // Locate White King
      sq:=0;
      while legalBoard.board[sq]<>WKING do inc(sq);
      is_legal:=not is_attacked(legalBoard,sq,true)
    end else begin
      // Locate Black King
      sq:=0;
      while legalBoard.board[sq]<>BKING do inc(sq);
      is_legal:=not is_attacked(legalBoard,sq,false)
    end
  end;

  procedure add_move(from,dest:byte);
  var test:_position;
  begin
    // If dest square is occupied by a friendly piece, exit
    if(movepos.whiteMove and(movepos.board[dest]in[WPAWN..WKING]))or(not movepos.whiteMove and(movepos.board[dest]in[BPAWN..BKING]))then exit;
    // Legality check and move add
    test:=movepos;
    test.board[dest]:=test.board[from];
    test.board[from]:=EMPTY;
    if is_legal(test)then begin
      // Promotions
      if(movepos.board[from]in[WPAWN,BPAWN])and(row_to_char(dest)in['1','8'])then begin
        inc(moves);
        generate_moves.san[moves]:=square_to_string(from)+square_to_string(dest)+'q';
        inc(moves);
        generate_moves.san[moves]:=square_to_string(from)+square_to_string(dest)+'r';
        inc(moves);
        generate_moves.san[moves]:=square_to_string(from)+square_to_string(dest)+'b';
        inc(moves);
        generate_moves.san[moves]:=square_to_string(from)+square_to_string(dest)+'n'
      end else inc(moves);
      generate_moves.san[moves]:=square_to_string(from)+square_to_string(dest)
    end
  end;
  
begin
  moves:=0;
  for square:=0 to 63 do if movepos.whiteMove then begin
    case movepos.board[square]of
      WPAWN:begin
        dest:=MOVETABLE_WP[0,square];
        if(dest<NO_MOVE)and(movepos.board[dest]in[BPAWN..BKING])then add_move(square,dest);
        dest:=MOVETABLE_WP[1,square];
        if(dest<NO_MOVE)and(movepos.board[dest]in[BPAWN..BKING])then add_move(square,dest);
        dest:=MOVETABLE_WP[2,square];
        if movepos.board[dest]=EMPTY then begin
          add_move(square,dest);
          dest:=MOVETABLE_WP[3,square];
          if(dest<NO_MOVE)and(movepos.board[dest]=EMPTY)then add_move(square,dest)
        end
      end;
      WKNIGHT:for index:=0 to 7 do if MOVETABLE_N[index,square]<NO_MOVE then add_move(square,MOVETABLE_N[index,square]);
      WKING:for index:=0 to 7 do if MOVETABLE_K[index,square]<NO_MOVE then add_move(square,MOVETABLE_K[index,square]);
      WBISHOP:begin
        index:=0;
        repeat
          dest:=MOVETABLE_B1[index,square];
          if dest<NO_MOVE then add_move(square,dest);
          inc(index)
        until(dest=NO_MOVE)or(movepos.board[dest]>EMPTY);
        index:=0;
        repeat
          dest:=MOVETABLE_B2[index,square];
          if dest<NO_MOVE then add_move(square,dest);
          inc(index)
        until(dest=NO_MOVE)or(movepos.board[dest]>EMPTY);
        index:=0;
        repeat
          dest:=MOVETABLE_B3[index,square];
          if dest<NO_MOVE then add_move(square,dest);
          inc(index)
        until(dest=NO_MOVE)or(movepos.board[dest]>EMPTY);
        index:=0;
        repeat
          dest:=MOVETABLE_B4[index,square];
          if dest<NO_MOVE then add_move(square,dest);
          inc(index)
        until(dest=NO_MOVE)or(movepos.board[dest]>EMPTY)
      end;
      WQUEEN:begin
        index:=0;
        repeat
          dest:=MOVETABLE_R1[index,square];
          if dest<NO_MOVE then add_move(square,dest);
          inc(index)
        until(dest=NO_MOVE)or(movepos.board[dest]>EMPTY);
        index:=0;
        repeat
          dest:=MOVETABLE_R2[index,square];
          if dest<NO_MOVE then add_move(square,dest);
          inc(index)
        until(dest=NO_MOVE)or(movepos.board[dest]>EMPTY);
        index:=0;
        repeat
          dest:=MOVETABLE_R3[index,square];
          if dest<NO_MOVE then add_move(square,dest);
          inc(index)
        until(dest=NO_MOVE)or(movepos.board[dest]>EMPTY);
        index:=0;
        repeat
          dest:=MOVETABLE_R4[index,square];
          if dest<NO_MOVE then add_move(square,dest);
          inc(index)
        until(dest=NO_MOVE)or(movepos.board[dest]>EMPTY);
        index:=0;
        repeat
          dest:=MOVETABLE_B1[index,square];
          if dest<NO_MOVE then add_move(square,dest);
          inc(index)
        until(dest=NO_MOVE)or(movepos.board[dest]>EMPTY);
        index:=0;
        repeat
          dest:=MOVETABLE_B2[index,square];
          if dest<NO_MOVE then add_move(square,dest);
          inc(index)
        until(dest=NO_MOVE)or(movepos.board[dest]>EMPTY);
        index:=0;
        repeat
          dest:=MOVETABLE_B3[index,square];
          if dest<NO_MOVE then add_move(square,dest);
          inc(index)
        until(dest=NO_MOVE)or(movepos.board[dest]>EMPTY);
        index:=0;
        repeat
          dest:=MOVETABLE_B4[index,square];
          if dest<NO_MOVE then add_move(square,dest);
          inc(index)
        until(dest=NO_MOVE)or(movepos.board[dest]>EMPTY)
      end;
      WROOK:begin
        index:=0;
        repeat
          dest:=MOVETABLE_R1[index,square];
          if dest<NO_MOVE then add_move(square,dest);
          inc(index)
        until(dest=NO_MOVE)or(movepos.board[dest]>EMPTY);
        index:=0;
        repeat
          dest:=MOVETABLE_R2[index,square];
          if dest<NO_MOVE then add_move(square,dest);
          inc(index)
        until(dest=NO_MOVE)or(movepos.board[dest]>EMPTY);
        index:=0;
        repeat
          dest:=MOVETABLE_R3[index,square];
          if dest<NO_MOVE then add_move(square,dest);
          inc(index)
        until(dest=NO_MOVE)or(movepos.board[dest]>EMPTY);
        index:=0;
        repeat
          dest:=MOVETABLE_R4[index,square];
          if dest<NO_MOVE then add_move(square,dest);
          inc(index)
        until(dest=NO_MOVE)or(movepos.board[dest]>EMPTY)
      end
    end
  end else begin
    case movepos.board[square]of
      BPAWN:begin
        dest:=MOVETABLE_BP[0,square];
        if(dest<NO_MOVE)and(movepos.board[dest]in[WPAWN..WKING])then add_move(square,dest);
        dest:=MOVETABLE_BP[1,square];
        if(dest<NO_MOVE)and(movepos.board[dest]in[WPAWN..WKING])then add_move(square,dest);
        dest:=MOVETABLE_BP[2,square];
        if movepos.board[dest]=EMPTY then begin
          add_move(square,dest);
          dest:=MOVETABLE_BP[3,square];
          if(dest<NO_MOVE)and(movepos.board[dest]=EMPTY)then add_move(square,dest)
        end
      end;
      BKNIGHT:for index:=0 to 7 do if MOVETABLE_N[index,square]<NO_MOVE then add_move(square,MOVETABLE_N[index,square]);
      BKING:for index:=0 to 7 do if MOVETABLE_K[index,square]<NO_MOVE then add_move(square,MOVETABLE_K[index,square]);
      BBISHOP:begin
        index:=0;
        repeat
          dest:=MOVETABLE_B1[index,square];
          if dest<NO_MOVE then add_move(square,dest);
          inc(index)
        until(dest=NO_MOVE)or(movepos.board[dest]>0);
        index:=0;
        repeat
          dest:=MOVETABLE_B2[index,square];
          if dest<NO_MOVE then add_move(square,dest);
          inc(index)
        until(dest=NO_MOVE)or(movepos.board[dest]>0);
        index:=0;
        repeat
          dest:=MOVETABLE_B3[index,square];
          if dest<NO_MOVE then add_move(square,dest);
          inc(index)
        until(dest=NO_MOVE)or(movepos.board[dest]>0);
        index:=0;
        repeat
          dest:=MOVETABLE_B4[index,square];
          if dest<NO_MOVE then add_move(square,dest);
          inc(index)
        until(dest=NO_MOVE)or(movepos.board[dest]>0)
      end;
      BQUEEN:begin
        index:=0;
        repeat
          dest:=MOVETABLE_R1[index,square];
          if dest<NO_MOVE then add_move(square,dest);
          inc(index)
        until(dest=NO_MOVE)or(movepos.board[dest]>0);
        index:=0;
        repeat
          dest:=MOVETABLE_R2[index,square];
          if dest<NO_MOVE then add_move(square,dest);
          inc(index)
        until(dest=NO_MOVE)or(movepos.board[dest]>0);
        index:=0;
        repeat
          dest:=MOVETABLE_R3[index,square];
          if dest<NO_MOVE then add_move(square,dest);
          inc(index)
        until(dest=NO_MOVE)or(movepos.board[dest]>0);
        index:=0;
        repeat
          dest:=MOVETABLE_R4[index,square];
          if dest<NO_MOVE then add_move(square,dest);
          inc(index)
        until(dest=NO_MOVE)or(movepos.board[dest]>0);
        index:=0;
        repeat
          dest:=MOVETABLE_B1[index,square];
          if dest<NO_MOVE then add_move(square,dest);
          inc(index)
        until(dest=NO_MOVE)or(movepos.board[dest]>0);
        index:=0;
        repeat
          dest:=MOVETABLE_B2[index,square];
          if dest<NO_MOVE then add_move(square,dest);
          inc(index)
        until(dest=NO_MOVE)or(movepos.board[dest]>0);
        index:=0;
        repeat
          dest:=MOVETABLE_B3[index,square];
          if dest<NO_MOVE then add_move(square,dest);
          inc(index)
        until(dest=NO_MOVE)or(movepos.board[dest]>0);
        index:=0;
        repeat
          dest:=MOVETABLE_B4[index,square];
          if dest<NO_MOVE then add_move(square,dest);
          inc(index)
        until(dest=NO_MOVE)or(movepos.board[dest]>0)
      end;
      BROOK:begin
        index:=0;
        repeat
          dest:=MOVETABLE_R1[index,square];
          if dest<NO_MOVE then add_move(square,dest);
          inc(index)
        until(dest=NO_MOVE)or(movepos.board[dest]>0);
        index:=0;
        repeat
          dest:=MOVETABLE_R2[index,square];
          if dest<NO_MOVE then add_move(square,dest);
          inc(index)
        until(dest=NO_MOVE)or(movepos.board[dest]>0);
        index:=0;
        repeat
          dest:=MOVETABLE_R3[index,square];
          if dest<NO_MOVE then add_move(square,dest);
          inc(index)
        until(dest=NO_MOVE)or(movepos.board[dest]>0);
        index:=0;
        repeat
          dest:=MOVETABLE_R4[index,square];
          if dest<NO_MOVE then add_move(square,dest);
          inc(index)
        until(dest=NO_MOVE)or(movepos.board[dest]>0)
      end
    end
  end;
  if movepos.whiteMove then begin
    // En-Passant
    case movepos.ep of
      0:if movepos.board[B5]=WPAWN then begin
        inc(moves);
        generate_moves.san[moves]:='ba6'
      end;
      1..6:if movepos.board[H4+movepos.ep]=WPAWN then begin
        inc(moves);
        generate_moves.san[moves]:=col_to_char(H4+movepos.ep)+square_to_string(A6+movepos.ep)
      end else if movepos.board[B5+movepos.ep]=WPAWN then begin
        inc(moves);
        generate_moves.san[moves]:=col_to_char(B5+movepos.ep)+square_to_string(A6+movepos.ep)
      end;
      7:if movepos.board[G5]=WPAWN then begin
        inc(moves);
        generate_moves.san[moves]:='gh6'
      end
    end;
    // Castles
    if movepos.wq and(movepos.board[B1]=EMPTY)and(movepos.board[C1]=EMPTY)and(movepos.board[D1]=EMPTY)and not is_attacked(movepos,C1,BLACK)and not is_attacked(movepos,D1,BLACK)and not is_attacked(movepos,E1,BLACK)then begin
      inc(moves);
      generate_moves.san[moves]:='e1c1'
    end;
    if movepos.wk and(movepos.board[F1]=EMPTY)and(movepos.board[G1]=EMPTY)and not is_attacked(movepos,G1,BLACK)and not is_attacked(movepos,F1,BLACK)and not is_attacked(movepos,E1,BLACK)then begin
      inc(moves);
      generate_moves.san[moves]:='e1g1'
    end
  end else begin
    // En-Passant
    case movepos.ep of
      0:if movepos.board[B4]=BPAWN then begin
        inc(moves);
        generate_moves.san[moves]:='ba3'
      end;
      1..6:if movepos.board[H3+movepos.ep]=BPAWN then begin
        inc(moves);
        generate_moves.san[moves]:=col_to_char(H3+movepos.ep)+square_to_string(A3+movepos.ep)
      end else if movepos.board[B4+movepos.ep]=BPAWN then begin
        inc(moves);
        generate_moves.san[moves]:=col_to_char(B4+movepos.ep)+square_to_string(A3+movepos.ep)
      end;
      7:if movepos.board[G4]=BPAWN then begin
        inc(moves);
        generate_moves.san[moves]:='gh3'
      end
    end;
    // Castles
    if movepos.bq and(movepos.board[B8]=EMPTY)and(movepos.board[C8]=EMPTY)and(movepos.board[D8]=EMPTY)and not is_attacked(movepos,C8,WHITE)and not is_attacked(movepos,D8,WHITE)and not is_attacked(movepos,E8,WHITE)then begin
      inc(moves);
      generate_moves.san[moves]:='e8c8'
    end;
    if movepos.bk and(movepos.board[F8]=EMPTY)and(movepos.board[G8]=EMPTY)and not is_attacked(movepos,G8,WHITE)and not is_attacked(movepos,F8,WHITE)and not is_attacked(movepos,E8,WHITE)then begin
      inc(moves);
      generate_moves.san[moves]:='e8g8'
    end
  end;
  generate_moves.num:=moves
end;

procedure make_move(var po:_position;mov:string);
var from,dest:byte;
begin
  from:=ord(mov[1])-97+(ord(mov[2])-49)*8;
  dest:=ord(mov[3])-97+(ord(mov[4])-49)*8;
  case po.whiteMove of
    true:begin
      // Update castle rights
      if po.wq and(from=A1)then po.wq:=false;
      if po.wk and(from=H1)then po.wk:=false;
      if(po.wk or po.wq)and(from=E1)then begin
        po.wq:=false;
        po.wk:=false
      end;
      // Update en-passant 
      if(po.board[from]=WPAWN)and(from in[A2..H2])and(dest=from+16)then po.ep:=from mod 8 else po.ep:=NO_MOVE;
      if(po.board[from]=WKING)and(from=E1)and(dest=C1)then begin
        // Make queenside castle
        po.board[A1]:=EMPTY;
        po.board[C1]:=WKING;
        po.board[D1]:=WROOK;
        po.board[E1]:=EMPTY
      end else if(po.board[from]=WKING)and(from=E1)and(dest=G1)then begin
        // Make kingside castle
        po.board[E1]:=EMPTY;
        po.board[F1]:=WROOK;
        po.board[G1]:=WKING;
        po.board[H1]:=EMPTY
      end else if(po.board[from]=WPAWN)and(col_to_char(from)<>col_to_char(dest))and(po.board[dest]=EMPTY)then begin
        // Make EP
        po.board[from]:=EMPTY;
        po.board[dest]:=WPAWN;
        po.board[dest-8]:=EMPTY
      end else if(po.board[from]=WPAWN)and(row_to_char(from)='7')then begin
        // Make promotion
        case mov[5]of
          'n':po.board[dest]:=WKNIGHT;
          'b':po.board[dest]:=WBISHOP;
          'r':po.board[dest]:=WROOK;
          'q':po.board[dest]:=WQUEEN
        end;
        po.board[from]:=EMPTY
      end else begin
        // Make normal moves
        po.board[dest]:=po.board[from];
        po.board[from]:=EMPTY
      end
    end;
    false:begin
      // Update castle rights
      if po.bq and(from=A8)then po.bq:=false;
      if po.bk and(from=H8)then po.bk:=false;
      if(po.bk or po.bq)and(from=E8)then begin
        po.bq:=false;
        po.bk:=false
      end;
      // Update castle rights
      if(po.board[from]=BPAWN)and(from in[A7..H7])and(dest=from-16)then po.ep:=from mod 8 else po.ep:=NO_MOVE;
        // Make queenside castle
      if(po.board[from]=BKING)and(from=E8)and(dest=C8)then begin
        po.board[A8]:=EMPTY;
        po.board[C8]:=BKING;
        po.board[D8]:=BROOK;
        po.board[E8]:=EMPTY
      end else if(po.board[from]=BKING)and(from=E8)and(dest=G8)then begin
        // Make kingside castle
        po.board[E8]:=EMPTY;
        po.board[F8]:=BROOK;
        po.board[G8]:=BKING;
        po.board[H8]:=EMPTY
      end else if(po.board[from]=BPAWN)and(col_to_char(from)<>col_to_char(dest))and(po.board[dest]=EMPTY)then begin
        // Make EP
        po.board[from]:=EMPTY;
        po.board[dest]:=BPAWN;
        po.board[dest+8]:=EMPTY
      end else if(po.board[from]=BPAWN)and(row_to_char(from)='2')then begin
        // Make promotion
        case mov[5]of
          'n':po.board[dest]:=BKNIGHT;
          'b':po.board[dest]:=BBISHOP;
          'r':po.board[dest]:=BROOK;
          'q':po.board[dest]:=BQUEEN
        end;
        po.board[from]:=EMPTY
      end else begin
        // Make normal moves
        po.board[dest]:=po.board[from];
        po.board[from]:=EMPTY
      end
    end
  end;
  po.whiteMove:=not po.whiteMove
end;

var command:string;
    noPlay,protejWhite:boolean;
    mainBoard:_position;

procedure search;
var list:_list;
    index:byte;
begin
  list:=generate_moves(mainBoard);
  if list.num=0 then begin
    noPlay:=true;
    exit
  end;
  index:=random(list.num)+1;
  make_move(mainBoard,list.san[index]);
  writeln('move '+list.san[index])
end;

procedure opponent_move(mov:string);
begin
  make_move(mainBoard,mov)
end;

begin
  init_movetable;
  randomize;
  noPlay:=true;
  writeln(VERSION);
  repeat
    if(not noPlay)and(protejWhite=mainBoard.whiteMove)then search;
    readln(command);
    if command='xboard'then
    else if command='new'then begin
      new_game(mainBoard);
      protejWhite:=false;
      noPlay:=false
    end else if command='quit'then exit
    else if command='force'then noPlay:=true
    else if command='go'then begin
      noPlay:=false;
      protejWhite:=mainBoard.whiteMove;
      search
    end else if command='playother'then begin
      noPlay:=false;
      protejWhite:=not mainBoard.whiteMove
    end else if command='white'then protejWhite:=false
    else if command='black'then protejWhite:=true
    else if command='hint'then writeln('Hint: not implemented')
    else if command='bk'then writeln(' bk not implemented'+#13+#10)
    else if copy(command,1,8)='protover'then writeln('feature usermove=1 myname="'+VERSION+'" variant="normal" colors=0 done=1')
    else if copy(command,1,8)='usermove'then opponent_move(copy(command,10,length(command)-9))
    else if copy(command,1,4)='ping'then writeln('pong'+copy(command,5,length(command)-4))
    else if copy(command,1,8)='rejected'then writeln('telluser '+command)
    else if command='undo'then
    else if command='draw'then
    else if command='edit'then
    else if command='random'then
    else if command='remove'then
    else if command='hard'then
    else if command='easy'then
    else if command='post'then
    else if command='nopost'then
    else if command='analyze'then
    else if copy(command,1,4)='time'then
    else if copy(command,1,8)='accepted'then
    else if copy(command,1,4)='name'then
    else if copy(command,1,5)='level'then
    else if copy(command,1,3)='st 'then
    else if copy(command,1,4)='otim'then
    else if copy(command,1,6)='result'then
    else if copy(command,1,8)='set_board'then
    else if copy(command,1,6)='rating'then
    else if copy(command,1,4)='ics 'then
    else writeln('telluser Unrecognized command: '+command)
  until command='quit'
end.
