// EXchess source code, (c) Daniel C. Homan  1997-2011
// Released under the GNU public license, see file license.txt

/* Execute move function */


#include "define.h"
#include "chess.h"
#include "const.h"
#include "funct.h"
#include "hash.h"
#include "extern.h"

// Function to execute the move.
// If the move is legal, the function returns a 1, otherwise 0.
// Note that the move is made, regardless - so proper precautions
// need to be taken, if the move might need to be undone
int position::exec_move(move emove, int ply)
{
  move_t mv = emove.b;
  register int pi;

  // if this is a castle, check that it is legal then
  // move the rook, the king move is given below
  if(mv.type&2) {
       if(check) return 0;
       switch(mv.to) {
	 /* white kingside castle */
           case 6:
              if(sq[5].type || sq[6].type ||
                  attacks(5,wtm^1,1))
                { return 0; }
              sq[5] = sq[7];
              sq[7] = empty;
              Or(hcode, hval[WROOK][5]);
              Or(hcode, hval[WROOK][7]);
              has_castled[1] = 1;
              /* update piece list */ 
              for(pi=1;pi<=plist[wtm][ROOK][0];pi++)
                if(plist[wtm][ROOK][pi] == 7) {
                   plist[wtm][ROOK][pi] = 5;
                   break;
                }
              break;
	 /* white queenside castle */  
           case 2:
              if(sq[1].type || sq[2].type || sq[3].type ||
                  attacks(3,wtm^1,1))
                { return 0; }
              sq[3] = sq[0];
              sq[0] = empty;
              Or(hcode, hval[WROOK][3]);
              Or(hcode, hval[WROOK][0]);
              has_castled[1] = 1;
              /* update piece list */
              for(pi=1;pi<=plist[wtm][ROOK][0];pi++)
                if(plist[wtm][ROOK][pi] == 0) {
                   plist[wtm][ROOK][pi] = 3;
                   break;
                }
              break;
	 /* black kingside castle */  
           case 62:
              if(sq[61].type || sq[62].type ||
                  attacks(61,wtm^1,1))
                { return 0; }
              sq[61] = sq[63];
              sq[63] = empty;
              Or(hcode, hval[BROOK][61]);
              Or(hcode, hval[BROOK][63]);
              has_castled[0] = 1;
              /* update piece list */
              for(pi=1;pi<=plist[wtm][ROOK][0];pi++)
                if(plist[wtm][ROOK][pi] == 63) {
                   plist[wtm][ROOK][pi] = 61;
                   break;
                }
              break;
	 /* black queenside castle */
           case 58:
              if(sq[57].type || sq[58].type || sq[59].type ||
                  attacks(59,wtm^1,1))
                { return 0; }
              sq[59] = sq[56];
              sq[56] = empty;
              Or(hcode, hval[BROOK][59]);
              Or(hcode, hval[BROOK][56]);
              has_castled[0] = 1;
              /* update piece list */
              for(pi=1;pi<=plist[wtm][ROOK][0];pi++)
                if(plist[wtm][ROOK][pi] == 56) {
                   plist[wtm][ROOK][pi] = 59;
                   break;
                }
              break;
           }
  }

  // update piece list for moving piece
  for(pi=1;pi<=plist[wtm][sq[mv.from].type][0];pi++)
   if(plist[wtm][sq[mv.from].type][pi] == mv.from) {
      plist[wtm][sq[mv.from].type][pi] = mv.to;
      break;
   }

  if(sq[mv.to].type) {
   // Remove hashcode for the target square 
   Or(hcode, hval[ID(sq[mv.to])][mv.to]);
   if(sq[mv.to].type == PAWN) { Or(pcode, hval[ID(sq[mv.to])][mv.to]); }   
   // Remove piece from piece list
   for(pi=1;pi<=plist[wtm^1][sq[mv.to].type][0];pi++)
    if(plist[wtm^1][sq[mv.to].type][pi] == mv.to) {
       plist[wtm^1][sq[mv.to].type][pi] =
       plist[wtm^1][sq[mv.to].type][plist[wtm^1][sq[mv.to].type][0]];
       plist[wtm^1][sq[mv.to].type][0]--;
       break;
    }
   // adjust material score   
   material += value[sq[mv.to].type];
   // adjust total piece count
   if(sq[mv.to].type > PAWN) pieces[wtm^1]--;
  }

  // Move the new piece to the target square
  sq[mv.to] = sq[mv.from];
  // Update the hash code to reflect the move
  Or(hcode, hval[ID(sq[mv.from])][mv.from]);
  Or(hcode, hval[ID(sq[mv.from])][mv.to]);
  if(sq[mv.from].type == PAWN) {
    Or(pcode, hval[ID(sq[mv.from])][mv.from]);
    Or(pcode, hval[ID(sq[mv.from])][mv.to]);
  }
  // Original square is now empty
  sq[mv.from] = empty;

  // if move is en-passant, finish it
  if(mv.type&EP) {
    if(wtm) {
      sq[mv.to-8] = empty;
      Or(hcode, hval[BPAWN][mv.to-8]);
      Or(pcode, hval[BPAWN][mv.to-8]);
      // Update piece lists 
      for(pi=1;pi<=plist[wtm^1][PAWN][0];pi++)
        if(plist[BLACK][PAWN][pi] == mv.to-8) {
           plist[BLACK][PAWN][pi] = 
           plist[BLACK][PAWN][plist[wtm^1][PAWN][0]];
	   plist[BLACK][PAWN][0]--;
           break;
        }
    } else {
      sq[mv.to+8] = empty;
      Or(hcode, hval[WPAWN][mv.to+8]);
      Or(pcode, hval[WPAWN][mv.to+8]);
      // Update piece lists
      for(pi=1;pi<=plist[wtm^1][PAWN][0];pi++)
       if(plist[WHITE][PAWN][pi] == mv.to+8) {
          plist[WHITE][PAWN][pi] = 
          plist[WHITE][PAWN][plist[wtm^1][PAWN][0]];
	  plist[WHITE][PAWN][0]--;
          break;
       }
    }
    material += value[PAWN];
  }

  // if we are in check, move isn't legal
  // return 0
  if(!check) {
   if(check || mv.type&EP || sq[mv.to].type == KING)
   {
     if(attacks(plist[wtm][KING][1], wtm^1, 1)) return 0;
   } else if(slide_check_table[mv.from][plist[wtm][KING][1]]) {
     if(simple_check(mv.from)) return 0;
   }
  }

  // if the move is a promotion, promote it
  if(mv.type&PROMOTE) {
    // Remove the pawn from the hash code
    Or(hcode, hval[ID(sq[mv.to])][mv.to]);
    Or(pcode, hval[ID(sq[mv.to])][mv.to]);
    // Change the piece type to the promoted piece
    sq[mv.to].type = mv.promote;
    // Add the new piece to the piece lists
    plist[wtm][mv.promote][0]++;
    plist[wtm][mv.promote][plist[wtm][mv.promote][0]]=mv.to;
    // Remove the pawn from the piece lists
    for(pi=1;pi<=plist[wtm][PAWN][0];pi++)
      if(plist[wtm][PAWN][pi] == mv.to) {
         plist[wtm][PAWN][pi] = 
         plist[wtm][PAWN][plist[wtm][PAWN][0]];
	 plist[wtm][PAWN][0]--;
         break;
      }
    // adjust material score
    material += value[mv.promote] - value[PAWN];
    // add piece to hash code 
    Or(hcode, hval[ID(sq[mv.to])][mv.to]);
    // adjust total piece count
    pieces[wtm]++;
  }

  // update position characteristics
  wtm = wtm^1;
  material = -material;
  Or(hcode, hbtm); Or(hcode, hwtm);

  // undo hash code for en-passant and castling status
  Or(hcode, ep_code[ep]);
  Or(hcode, castle_code[castle]);

  // if move is a pawn push 2 spaces, set en passant flag
  if((mv.type&PAWN_PUSH2) && 
     ((FILE(mv.to) < 7 && sq[mv.to+1].type == PAWN && sq[mv.to+1].side == wtm)
      || (FILE(mv.to) && sq[mv.to-1].type == PAWN && sq[mv.to-1].side == wtm)))
   { ep = (mv.from+mv.to)/2; } else { ep = 0; }
  // if move is not a capture or a pawn move, increase fifty count
  if(mv.type&CAPTURE || mv.type&PAWN_PUSH2 || mv.type&PAWN_PUSH)
   { fifty = 0; } else { fifty++; }
  // update castling status
  castle = castle&castle_mask[mv.from];
  castle = castle&castle_mask[mv.to];
  // put this move in as the last move
  last = emove;

  // update hash code for en-passant and castling status
  Or(hcode, ep_code[ep]);
  Or(hcode, castle_code[castle]);

  // check whether other side is placed in check
  int ptype = sq[mv.to].type; 
  int ksq = plist[wtm][KING][1];

  check = 0;

  if((ptype == BISHOP || ptype == QUEEN)
      && bishop_check_table[mv.to][ksq])
    check = dia_slide_attack(mv.to, ksq);
    //check = dia_attacks(ksq, wtm^1, 1);
  else if((ptype == ROOK || ptype == QUEEN)
         && rook_check_table[mv.to][ksq])
    check = hor_slide_attack(mv.to, ksq);
    //check = hor_attacks(ksq, wtm^1, 1);
  else if(ptype == PAWN && bishop_check_table[mv.to][ksq]) {
    // note the bishop_check_table guarantees that the pawn/king
    //   relationships below are OK... otherwise we would have to
    //   restrict the files for these comparisons
    if(wtm && (mv.to == ksq+9 || mv.to == ksq+7))
      { check = 1; return 1; }
    else if(wtm^1 && (mv.to == ksq-9 || mv.to == ksq-7))
      { check = 1; return 1; }
  } else if(ptype == KNIGHT && knight_check_table[mv.to][ksq]) check = 1;

  if(slide_check_table[mv.from][ksq] && check^1) 
    check = simple_check(mv.from);
  if(mv.type&EP && check^1)
    check = attacks(ksq, wtm^1, 1);
  if(mv.type&CASTLE && check^1)
    check = hor_attacks(ksq, wtm^1, 1);

  return 1;
}

// Same as above, but without updates to position hash code
// -- pawn hash code is still updated
int position::qexec_move(move emove, int ply)
{
  move_t mv = emove.b;
  register int pi;

  // if this is a castle, check that it is legal then
  // move the rook, the king move is given below
  if(mv.type&2) {
       if(check) return 0;
       switch(mv.to) {
	 /* white kingside castle */
           case 6:
              if(sq[5].type || sq[6].type ||
                  attacks(5,wtm^1,1))
                { return 0; }
              sq[5] = sq[7];
              sq[7] = empty;
              has_castled[1] = 1;
              /* update piece list */ 
              for(pi=1;pi<=plist[wtm][ROOK][0];pi++)
                if(plist[wtm][ROOK][pi] == 7) {
                   plist[wtm][ROOK][pi] = 5;
                   break;
                }
              break;
	 /* white queenside castle */  
           case 2:
              if(sq[1].type || sq[2].type || sq[3].type ||
                  attacks(3,wtm^1,1))
                { return 0; }
              sq[3] = sq[0];
              sq[0] = empty;
              has_castled[1] = 1;
              /* update piece list */
              for(pi=1;pi<=plist[wtm][ROOK][0];pi++)
                if(plist[wtm][ROOK][pi] == 0) {
                   plist[wtm][ROOK][pi] = 3;
                   break;
                }
              break;
	 /* black kingside castle */  
           case 62:
              if(sq[61].type || sq[62].type ||
                  attacks(61,wtm^1,1))
                { return 0; }
              sq[61] = sq[63];
              sq[63] = empty;
              has_castled[0] = 1;
              /* update piece list */
              for(pi=1;pi<=plist[wtm][ROOK][0];pi++)
                if(plist[wtm][ROOK][pi] == 63) {
                   plist[wtm][ROOK][pi] = 61;
                   break;
                }
              break;
	 /* black queenside castle */
           case 58:
              if(sq[57].type || sq[58].type || sq[59].type ||
                  attacks(59,wtm^1,1))
                { return 0; }
              sq[59] = sq[56];
              sq[56] = empty;
              has_castled[0] = 1;
              /* update piece list */
              for(pi=1;pi<=plist[wtm][ROOK][0];pi++)
                if(plist[wtm][ROOK][pi] == 56) {
                   plist[wtm][ROOK][pi] = 59;
                   break;
                }
              break;
           }
  }

  // update piece list for moving piece
  for(pi=1;pi<=plist[wtm][sq[mv.from].type][0];pi++)
   if(plist[wtm][sq[mv.from].type][pi] == mv.from) {
      plist[wtm][sq[mv.from].type][pi] = mv.to;
      break;
   }

  if(sq[mv.to].type) {
   // Remove hashcode for the target square 
   if(sq[mv.to].type == PAWN) { Or(pcode, hval[ID(sq[mv.to])][mv.to]); }    
   // Remove piece from piece list
   for(pi=1;pi<=plist[wtm^1][sq[mv.to].type][0];pi++)
    if(plist[wtm^1][sq[mv.to].type][pi] == mv.to) {
       plist[wtm^1][sq[mv.to].type][pi] =
       plist[wtm^1][sq[mv.to].type][plist[wtm^1][sq[mv.to].type][0]];
       plist[wtm^1][sq[mv.to].type][0]--;
       break;
    }
   // adjust material score   
   material += value[sq[mv.to].type];
   // adjust total piece count
   if(sq[mv.to].type > PAWN) pieces[wtm^1]--;
  }

  // Move the new piece to the target square
  sq[mv.to] = sq[mv.from];
  // Update the hash code to reflect the move
  if(sq[mv.from].type == PAWN) {
    Or(pcode, hval[ID(sq[mv.from])][mv.from]);
    Or(pcode, hval[ID(sq[mv.from])][mv.to]);
  }
  // Original square is now empty
  sq[mv.from] = empty;

  // if move is en-passant, finish it
  if(mv.type&EP) {
    if(wtm) {
      sq[mv.to-8] = empty;
      Or(pcode, hval[BPAWN][mv.to-8]);
      // Update piece lists 
      for(pi=1;pi<=plist[wtm^1][PAWN][0];pi++)
        if(plist[BLACK][PAWN][pi] == mv.to-8) {
           plist[BLACK][PAWN][pi] = 
           plist[BLACK][PAWN][plist[wtm^1][PAWN][0]];
	   plist[BLACK][PAWN][0]--;
           break;
        }
    } else {
      sq[mv.to+8] = empty;
      Or(pcode, hval[WPAWN][mv.to+8]);
      // Update piece lists
      for(pi=1;pi<=plist[wtm^1][PAWN][0];pi++)
       if(plist[WHITE][PAWN][pi] == mv.to+8) {
          plist[WHITE][PAWN][pi] = 
          plist[WHITE][PAWN][plist[wtm^1][PAWN][0]];
	  plist[WHITE][PAWN][0]--;
          break;
       }
    }
    material += value[PAWN];
  }

  // if we are in check, move isn't legal
  // return 0
  if(!check) {
    if(check || mv.type&EP || sq[mv.to].type == KING)
      {
	if(attacks(plist[wtm][KING][1], wtm^1, 1)) return 0;
      } else if(slide_check_table[mv.from][plist[wtm][KING][1]]) {
        if(simple_check(mv.from)) return 0;
      }
  }

  // if the move is a promotion, promote it
  if(mv.type&PROMOTE) {
    // Remove the pawn from the hash code
    Or(pcode, hval[ID(sq[mv.to])][mv.to]);
    // Change the piece type to the promoted piece
    sq[mv.to].type = mv.promote;
    // Add the new piece to the piece lists
    plist[wtm][mv.promote][0]++;
    plist[wtm][mv.promote][plist[wtm][mv.promote][0]]=mv.to;
    // Remove the pawn from the piece lists
    for(pi=1;pi<=plist[wtm][PAWN][0];pi++)
      if(plist[wtm][PAWN][pi] == mv.to) {
         plist[wtm][PAWN][pi] = 
         plist[wtm][PAWN][plist[wtm][PAWN][0]];
	 plist[wtm][PAWN][0]--;
         break;
      }
    // adjust material score
    material += value[mv.promote] - value[PAWN];
    // adjust total piece count
    pieces[wtm]++;
  }

  // update position characteristics
  wtm = wtm^1;
  material = -material;

  // if move is a pawn push 2 spaces, set en passant flag
  if((mv.type&PAWN_PUSH2) && 
     ((FILE(mv.to) < 7 && sq[mv.to+1].type == PAWN && sq[mv.to+1].side == wtm)
      || (FILE(mv.to) && sq[mv.to-1].type == PAWN && sq[mv.to-1].side == wtm)))
   { ep = (mv.from+mv.to)/2; } else { ep = 0; }
  // if move is not a capture or a pawn move, increase fifty count
  if(mv.type&CAPTURE || mv.type&PAWN_PUSH2 || mv.type&PAWN_PUSH)
   { fifty = 0; } else { fifty++; }
  // update castling status
  castle = castle&castle_mask[mv.from];
  castle = castle&castle_mask[mv.to];
  // put this move in as the last move
  last = emove;

  // check whether other side is placed in check
  int ptype = sq[mv.to].type; 
  int ksq = plist[wtm][KING][1];

  check = 0;

  if((ptype == BISHOP || ptype == QUEEN)
      && bishop_check_table[mv.to][ksq])
    check = dia_slide_attack(mv.to, ksq);
    //check = dia_attacks(ksq, wtm^1, 1);
  else if((ptype == ROOK || ptype == QUEEN)
         && rook_check_table[mv.to][ksq])
    check = hor_slide_attack(mv.to, ksq);
    //check = hor_attacks(ksq, wtm^1, 1);
  else if(ptype == PAWN && bishop_check_table[mv.to][ksq]) {
    // note the bishop_check_table guarantees that the pawn/king
    //   relationships below are OK... otherwise we would have to
    //   restrict the files for these comparisons
    if(wtm && (mv.to == ksq+9 || mv.to == ksq+7))
      { check = 1; return 1; }
    else if(wtm^1 && (mv.to == ksq-9 || mv.to == ksq-7))
      { check = 1; return 1; }
  } else if(ptype == KNIGHT && knight_check_table[mv.to][ksq]) check = 1;

  if(slide_check_table[mv.from][ksq] && check^1) 
    check = simple_check(mv.from);
  if(mv.type&EP && check^1)
    check = attacks(ksq, wtm^1, 1);
  if(mv.type&CASTLE && check^1)
    check = hor_attacks(ksq, wtm^1, 1);

  return 1;
}







