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

/* Attack functions */


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

/* Function to do a simple look to see if we are in check */
// Works by checking for a revealed attack from beyond
// square "move_sq" which was vacated on the previous move
int position::simple_check(int move_sq)
{
  int ksq = plist[wtm][KING][1];  // king-square

  if(rook_check_table[move_sq][ksq]) return hor_slide_attack_xray(ksq, move_sq);
  else if(bishop_check_table[move_sq][ksq]) return dia_slide_attack_xray(ksq, move_sq);
  else return 0;

}

// function to determine if squares A and B can
// have a sliding attack between them
int position::dia_slide_attack(int A, int B) {
  register int Ax = FILE(A), Ay = RANK(A);
  register int Bx = FILE(B), By = RANK(B);
  register int test_sq;

  if(Ax > Bx)
    if(Ay > By && A > B+9) 
      for(test_sq = A-9; test_sq > B; test_sq -= 9) 
	if(sq[test_sq].type) return 0;
  
  if(Ax > Bx)
    if(Ay < By && A < B-7) 
      for(test_sq = A+7; test_sq < B; test_sq += 7) 
	if(sq[test_sq].type) return 0;

  if(Ax < Bx)
    if(Ay > By && A > B+7) 
      for(test_sq = A-7; test_sq > B; test_sq -= 7) 
	if(sq[test_sq].type) return 0;
  
  if(Ax < Bx)
    if(Ay < By && A < B-9) 
      for(test_sq = A+9; test_sq < B; test_sq += 9) 
	if(sq[test_sq].type) return 0;
 
  return 1;   // if there are no blockers the two squares
              // can be connected by a sliding attack
  
}

// function to determine if squares A and B can
// have a sliding attack between them with the 
// attacker (attacking square A) lies 
// somewhere beyond square B
int position::dia_slide_attack_xray(int A, int B) {
  register int Ax = FILE(A), Ay = RANK(A);
  register int Bx = FILE(B), By = RANK(B);
  register int test_sq;

  if(Ax > Bx)
    if(Ay > By) {
      for(test_sq = A-9; test_sq > B; test_sq -= 9) 
	if(sq[test_sq].type) return 0;
      for(test_sq = B-9; test_sq >= 0 && FILE(test_sq) < 7; test_sq -= 9) {
	if(!sq[test_sq].type) continue; 
	if(sq[test_sq].side != sq[A].side && (sq[test_sq].type == QUEEN || sq[test_sq].type == BISHOP)) return 1;
	else return 0;
      }
      return 0;
    }
  
  if(Ax > Bx)
    if(Ay < By) {
      for(test_sq = A+7; test_sq < B; test_sq += 7) 
	if(sq[test_sq].type) return 0;
      for(test_sq = B+7; test_sq <= 63 && FILE(test_sq) < 7; test_sq += 7) {
	if(!sq[test_sq].type) continue; 
	if(sq[test_sq].side != sq[A].side && (sq[test_sq].type == QUEEN || sq[test_sq].type == BISHOP)) return 1;
	else return 0;
      }
      return 0;
    }

  if(Ax < Bx)
    if(Ay > By) {
      for(test_sq = A-7; test_sq > B; test_sq -= 7) 
	if(sq[test_sq].type) return 0;
      for(test_sq = B-7; test_sq >= 0 && FILE(test_sq) > 0; test_sq -= 7) {
	if(!sq[test_sq].type) continue; 
	if(sq[test_sq].side != sq[A].side && (sq[test_sq].type == QUEEN || sq[test_sq].type == BISHOP)) return 1;
	else return 0;
      }
      return 0;
    }
  
  if(Ax < Bx)
    if(Ay < By) {
      for(test_sq = A+9; test_sq < B; test_sq += 9) 
	if(sq[test_sq].type) return 0;
      for(test_sq = B+9; test_sq <= 63 && FILE(test_sq) > 0; test_sq += 9) {
	if(!sq[test_sq].type) continue; 
	if(sq[test_sq].side != sq[A].side && (sq[test_sq].type == QUEEN || sq[test_sq].type == BISHOP)) return 1;
	else return 0;
      }
      return 0;
    }
 
  return 0;  
  
}

// function to determine if squares A and B can
// have a sliding attack between them
int position::hor_slide_attack(int A, int B) {
  register int Ax = FILE(A), Ay = RANK(A);
  register int Bx = FILE(B), By = RANK(B);
  register int test_sq;

  if(Ax > Bx+1)
      for(test_sq = A-1; test_sq > B; test_sq -= 1) 
	if(sq[test_sq].type) return 0;

  if(Ax < Bx-1)
      for(test_sq = A+1; test_sq < B; test_sq += 1) 
	if(sq[test_sq].type) return 0;
 
  if(Ay > By+1)
      for(test_sq = A-8; test_sq > B; test_sq -= 8) 
	if(sq[test_sq].type) return 0;

  if(Ay < By-1)
      for(test_sq = A+8; test_sq < B; test_sq += 8) 
	if(sq[test_sq].type) return 0;
 
  return 1;   // if there are no blockers the two squares
              // can be connected by a sliding attack
  
}


// function to determine if squares A and B can
// have a sliding attack between them with the 
// attacker (attacking square A) lies 
// somewhere beyond square B
int position::hor_slide_attack_xray(int A, int B) {
  register int Ax = FILE(A), Ay = RANK(A);
  register int Bx = FILE(B), By = RANK(B);
  register int test_sq;

  if(Ax > Bx) {
      for(test_sq = A-1; test_sq > B; test_sq -= 1) 
	if(sq[test_sq].type) return 0;
      for(test_sq = B-1; test_sq >= 0 && FILE(test_sq) < 7; test_sq -=1) {
	if(!sq[test_sq].type) continue; 
	if(sq[test_sq].side != sq[A].side && (sq[test_sq].type == QUEEN || sq[test_sq].type == ROOK)) return 1;
	else return 0;
      }
      return 0;
    }

  if(Ax < Bx) {
      for(test_sq = A+1; test_sq < B; test_sq += 1) 
	if(sq[test_sq].type) return 0;
      for(test_sq = B+1; test_sq <= 63 && FILE(test_sq) > 0; test_sq +=1) {
	if(!sq[test_sq].type) continue; 
	if(sq[test_sq].side != sq[A].side && (sq[test_sq].type == QUEEN || sq[test_sq].type == ROOK)) return 1;
	else return 0;
      }
      return 0;
    }
 
  if(Ay > By) {
      for(test_sq = A-8; test_sq > B; test_sq -= 8) 
	if(sq[test_sq].type) return 0;
      for(test_sq = B-8; test_sq >=0; test_sq -= 8) {
	if(!sq[test_sq].type) continue; 
	if(sq[test_sq].side != sq[A].side && (sq[test_sq].type == QUEEN || sq[test_sq].type == ROOK)) return 1;
	else return 0;
      }
      return 0;
    }
 
  if(Ay < By) {
      for(test_sq = A+8; test_sq < B; test_sq += 8) 
	if(sq[test_sq].type) return 0;
      for(test_sq = B+8; test_sq <=63; test_sq += 8) {
	if(!sq[test_sq].type) continue; 
	if(sq[test_sq].side != sq[A].side && (sq[test_sq].type == QUEEN || sq[test_sq].type == ROOK)) return 1;
	else return 0;
      }
      return 0;
    }

  return 0;
  
}

/*---------------- Driver to calculate Attacks --------------*/
// If the "one" parameter is true, this means we only need
// to find one attack, then exit.  "side" is the side which
// is doing the attacking.
int position::attacks(int sqr, int side, int one)
{
  int attacks = 0;

  attacks += dia_attacks(sqr, side, one);
  if(attacks && one) return 1;
  attacks += hor_attacks(sqr, side, one);
  if(attacks && one) return 1;
  attacks += knt_attacks(sqr, side, one);

  return attacks;
}

/*----------------- Calculate diagonal attacks ------------------*/

int position::dia_attacks(int sqr, int side, int one)
{
  int attacks = 0, other = side^1, mm = FILE(sqr), nn = RANK(sqr), tsq;

  int ii = 1;
  while (mm + ii <= 7 && nn + ii <= 7)
  {
   tsq = SQR((mm+ii),(nn+ii));
   if (sq[tsq].side == side)
   {
    if (ii == 1 && ID(sq[tsq]) == BPAWN)
    { if(one) return 1; attacks++; }
    else if (ii == 1 && sq[tsq].type == KING)
    { if(one) return 1; attacks++; }
    else if (sq[tsq].type == QUEEN || sq[tsq].type == BISHOP)
    { if(one) return 1; attacks++; }
    break;
   }
   if (sq[tsq].side == other) break;
   ii++;
  }

  ii = 1;
  while ((mm - ii) >= 0 && nn + ii <= 7)
  {
   tsq = SQR((mm-ii),(nn+ii));
   if (sq[tsq].side == side)
   {
    if (ii == 1 && ID(sq[tsq]) == BPAWN)
    { if(one) return 1; attacks++; }
    else if (ii == 1 && sq[tsq].type == KING)
    { if(one) return 1; attacks++; }
    else if (sq[tsq].type == QUEEN || sq[tsq].type == BISHOP)
    { if(one) return 1; attacks++; }
    break;
   }
   if (sq[tsq].side == other) break;
   ii++;
  }

  ii = 1;
  while ((mm - ii) >= 0 && (nn - ii) >= 0)
  {
   tsq = SQR((mm-ii),(nn-ii));
   if (sq[tsq].side == side)
   {
    if (ii == 1 && ID(sq[tsq]) == WPAWN)
    { if(one) return 1; attacks++; }
    else if (ii == 1 && sq[tsq].type == KING)
    { if(one) return 1; attacks++; }
    else if (sq[tsq].type == QUEEN || sq[tsq].type == BISHOP)
    { if(one) return 1; attacks++; }
    break;
   }
   if (sq[tsq].side == other) break;
   ii++;
  }

  ii = 1;
  while (mm + ii <= 7 && nn - ii >= 0)
  {
   tsq = SQR((mm+ii),(nn-ii));
   if (sq[tsq].side == side)
   {
    if (ii == 1 && ID(sq[tsq]) == WPAWN)
    { if(one) return 1; attacks++; }
    else if (ii == 1 && sq[tsq].type == KING)
    { if(one) return 1; attacks++; }
    else if (sq[tsq].type == QUEEN || sq[tsq].type == BISHOP)
    { if(one) return 1; attacks++; }
    break;
   }
   if (sq[tsq].side == other) break;
   ii++;
  }


  return attacks;
}

/*----------------- Calculate Horizontal attacks ------------------*/

int position::hor_attacks(int sqr, int side, int one)
{
  int attacks = 0, other = side^1, mm = FILE(sqr), nn = RANK(sqr), tsq;

  int ii = 1;
  while (mm + ii <= 7)
  {
   tsq = SQR((mm+ii), nn);
   if (sq[tsq].side == side)
   {
    if (ii == 1 && sq[tsq].type == KING)
    { if(one) return 1; attacks++; }
    else if (sq[tsq].type == QUEEN || sq[tsq].type == ROOK)
    { if(one) return 1; attacks++; }
    break;
   }
   if (sq[tsq].side == other) break;
   ii++;
  }

  ii = 1;
  while ((mm - ii) >= 0)
  {
   tsq = SQR((mm-ii), nn);
   if (sq[tsq].side == side)
   {
    if (ii == 1 && sq[tsq].type == KING)
    { if(one) return 1; attacks++; }
    else if (sq[tsq].type == QUEEN || sq[tsq].type == ROOK)
    { if(one) return 1; attacks++; }
    break;
   }
   if (sq[tsq].side == other) break;
   ii++;
  }

  ii = 1;
  while (nn - ii >= 0)
  {
   tsq = SQR((mm),(nn-ii));
   if (sq[tsq].side == side)
   {
    if (ii == 1 && sq[tsq].type == KING)
    { if(one) return 1; attacks++; }
    else if (sq[tsq].type == QUEEN || sq[tsq].type == ROOK)
    { if(one) return 1; attacks++; }
    break;
   }
   if (sq[tsq].side == other) break;
   ii++;
  }

  ii = 1;
  while (nn + ii <= 7)
  {
   tsq = SQR((mm),(nn+ii));
   if (sq[tsq].side == side)
   {
    if (ii == 1 && sq[tsq].type == KING)
    { if(one) return 1; attacks++; }
    else if (sq[tsq].type == QUEEN || sq[tsq].type == ROOK)
    { if(one) return 1; attacks++; }
    break;
   }
   if (sq[tsq].side == other) break;
   ii++;
  }


  return attacks;
}

/*------------------------ Knight Attacks Counted -------------------*/
int position::knt_attacks(int sqr, int side, int one)
{
  int self = side^1;
  int tsq, attacks = 0;

  if(FILE(sqr) < 6 && RANK(sqr) < 7) {
   tsq = sqr + 10;
   if(sq[tsq].side != self) {
     if(sq[tsq].type == KNIGHT) { if(one) return 1; attacks++; }
    }
  }
  if(FILE(sqr) < 6 && RANK(sqr)) {
   tsq = sqr - 6;
   if(sq[tsq].side != self) {
     if(sq[tsq].type == KNIGHT) { if(one) return 1; attacks++; }
    }
  }
  if(FILE(sqr) > 1 && RANK(sqr) < 7) {
   tsq = sqr + 6;
   if(sq[tsq].side != self) {
     if(sq[tsq].type == KNIGHT) { if(one) return 1; attacks++; }
    }
  }
  if(FILE(sqr) > 1 && RANK(sqr)) {
   tsq = sqr - 10;
   if(sq[tsq].side != self) {
     if(sq[tsq].type == KNIGHT) { if(one) return 1; attacks++; }
    }
  }
  if(FILE(sqr) < 7 && RANK(sqr) < 6) {
   tsq = sqr + 17;
   if(sq[tsq].side != self) {
     if(sq[tsq].type == KNIGHT) { if(one) return 1; attacks++; }
    }
  }
  if(FILE(sqr) && RANK(sqr) < 6) {
   tsq = sqr + 15;
   if(sq[tsq].side != self) {
     if(sq[tsq].type == KNIGHT) { if(one) return 1; attacks++; }
    }
  }
  if(FILE(sqr) < 7 && RANK(sqr) > 1) {
   tsq = sqr - 15;
   if(sq[tsq].side != self) {
     if(sq[tsq].type == KNIGHT) { if(one) return 1; attacks++; }
    }
  }
  if(FILE(sqr) && RANK(sqr) > 1) {
   tsq = sqr - 17;
   if(sq[tsq].side != self) {
     if(sq[tsq].type == KNIGHT) { if(one) return 1; attacks++; }
    }
  }
 
 return attacks;
}

