Difference between revisions of "CPW King"

From Chessprogramming wiki
Jump to: navigation, search
(Created page with "'''Home * Engines * CPW-Engine * CPW_King''' This is a patch to the CPW-Engine, changing its evaluation function. Modifications include: * getting r...")
 
Line 16: Line 16:
 
<pre>
 
<pre>
 
void setSquaresNearKing() {
 
void setSquaresNearKing() {
    for (int i = 0; i < 128; ++i)
+
  for (int i = 0; i < 128; ++i)
    for (int j = 0; j < 128; ++j)
+
    for (int j = 0; j < 128; ++j)
    {
+
    {
  
    e.sqNearK[WHITE][i][j] = 0;
+
      e.sqNearK[WHITE][i][j] = 0;
    e.sqNearK[BLACK][i][j] = 0;
+
      e.sqNearK[BLACK][i][j] = 0;
  
    if ( IS_SQ(i) &&
+
      if (IS_SQ(i) &&
          IS_SQ(j) ) {
+
        IS_SQ(j)) {
  
          // squares constituting the ring around both kings
+
        // squares constituting the ring around both kings
  
          if (j == i + NORTH || j == i + SOUTH ||
+
        if (j == i + NORTH || j == i + SOUTH ||
              j == i + EAST || j == i + WEST ||
+
          j == i + EAST || j == i + WEST ||
              j == i + NW   || j == i + NE   ||
+
          j == i + NW || j == i + NE ||
              j == i + SW   || j == i + SE ) {
+
          j == i + SW || j == i + SE) {
  
              e.sqNearK[WHITE][i][j] = 1;
+
          e.sqNearK[WHITE][i][j] = 1;
              e.sqNearK[BLACK][i][j] = 1;
+
          e.sqNearK[BLACK][i][j] = 1;
          }
+
        }
  
          /* squares in front of the white king ring */
+
        /* squares in front of the white king ring */
  
          if ( j == i + NORTH + NORTH ||
+
        if (j == i + NORTH + NORTH ||
              j == i + NORTH + NE   ||
+
          j == i + NORTH + NE ||
              j == i + NORTH + NW )
+
          j == i + NORTH + NW)
              e.sqNearK[WHITE] [i] [j] = 1;
+
          e.sqNearK[WHITE][i][j] = 1;
  
          // squares in front og the black king ring
+
        // squares in front og the black king ring
  
          if ( j == i + SOUTH + SOUTH ||
+
        if (j == i + SOUTH + SOUTH ||
              j == i + SOUTH + SE   ||
+
          j == i + SOUTH + SE ||
              j == i + SOUTH + SW )
+
          j == i + SOUTH + SW)
              e.sqNearK[WHITE] [i] [j] = 1;
+
          e.sqNearK[WHITE][i][j] = 1;
    }
+
      }
 
+
    }
    }
 
 
}
 
}
 
 
 
</pre>
 
</pre>
  

Revision as of 14:43, 18 December 2018

Home * Engines * CPW-Engine * CPW_King

This is a patch to the CPW-Engine, changing its evaluation function. Modifications include:

  • getting rid of lazy eval
  • using eval hash table as default
  • modifying mobility functions, so that they will count number of attacks on squares near enemy king as well as the number of attackers
  • placing those functions in the piece-specific routines, before king safety calculation, so that it can make use of them
  • adding functions to count attacks by queens

Resulting program plays at the roughly the same strength as regular CPW-Engine, but is much more entertaining to watch. To obtain optimal results, piece/square values fo bishops and knights should be raised a tiny bit.

The most obvious drawback of the presented code is that it does not see attackers that are lined up, such as rooks doubled on a file. If You consider making this example into a real program, this is the first thing to change.

We need to add the following function to the eval_init.cpp, in order to define squares which are considered worthwhile to attack:

void setSquaresNearKing() {
  for (int i = 0; i < 128; ++i)
    for (int j = 0; j < 128; ++j)
    {

      e.sqNearK[WHITE][i][j] = 0;
      e.sqNearK[BLACK][i][j] = 0;

      if (IS_SQ(i) &&
        IS_SQ(j)) {

        // squares constituting the ring around both kings

        if (j == i + NORTH || j == i + SOUTH ||
          j == i + EAST || j == i + WEST ||
          j == i + NW || j == i + NE ||
          j == i + SW || j == i + SE) {

          e.sqNearK[WHITE][i][j] = 1;
          e.sqNearK[BLACK][i][j] = 1;
        }

        /* squares in front of the white king ring */

        if (j == i + NORTH + NORTH ||
          j == i + NORTH + NE ||
          j == i + NORTH + NW)
          e.sqNearK[WHITE][i][j] = 1;

        // squares in front og the black king ring

        if (j == i + SOUTH + SOUTH ||
          j == i + SOUTH + SE ||
          j == i + SOUTH + SW)
          e.sqNearK[WHITE][i][j] = 1;
      }
    }
}

The complete eval.cpp looks as follows:

#include "stdafx.h"
#include "0x88_math.h"
#include "eval.h"
#include "transposition.h"

#define use_eval_hash

/* mobility values for various piece kinds */
int bish_mob[16]  = { -10, -4, 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 7, 8 };
int rook_mob[16]  = { -4,  -2, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4 };
int knight_mob[9] = { -6,  -4, 0, 2, 4, 5, 6, 7, 8};

/* adjustements of piece value based on the number of own pawns */
int knight_adj[9] = { -20, -16, -12, -8, -4,  0,  4,  8, 12};
int rook_adj[9] =   {  15,  12,   9,  6,  3,  0, -3, -6, -9};

#define MINOR_ATT 2
#define ROOK_ATT  4
#define QUEEN_ATT 8

/*******************************************************************
*  This struct holds data about certain aspects of evaluation,     *
*  which allows program to print them if desired.                  *
*******************************************************************/

struct eval_vector {
int MaterialAdjustement[2];
int Blockages[2];
int PositionalThemes[2];
int Mobility[2];
int KingAttackers[2]; // no. of pieces attacking enemy king
int KingPressure[2];  // value of king pressure
} v;

/* local lists of pieces and pawns and indexes to them */
U8 pieceList[32];
U8 pieceIndex;
U8 pawnList[32];
U8 pawnIndex;

/* global flag used by mobility/attack detection functions to know
   if a piece examined currently is a king attacker */

int isAttacker = 0;

/*****************************************************************************
*                  Fast evaluation (material + pcsq + pawn structure)        *
*****************************************************************************/

int fast_eval() {

    /* fold in incrementally updated values */
    int result = p.PieceMaterial[WHITE] + p.PawnMaterial[WHITE] + p.Pcsq[WHITE]
               - p.PieceMaterial[BLACK] - p.PawnMaterial[BLACK] - p.Pcsq[BLACK];

    /* evaluate pawn structure, remembering that a pawn list must be set */
    result += getPawnScoreNoList();

    /* return score relative to the side to move */
    if ( b.stm == BLACK )
       return -result;
    else
       return result;
}


/*****************************************************************************
*                  1. Main evaluation function                               *
*****************************************************************************/

int eval( int alpha, int beta ) {
    int result;

    /* probe the evaluatinon hashtable */
    int probeval = tteval_probe();
    if (probeval != INVALID)
        return probeval;

    /* set internal data of the evaluation function */
    eval_setPieceLists();
    eval_clearVector();

    /* sum the incrementally counted material and pcsq values */
    result = p.PieceMaterial[WHITE] + p.PawnMaterial[WHITE] + p.Pcsq[WHITE]
           - p.PieceMaterial[BLACK] - p.PawnMaterial[BLACK] - p.Pcsq[BLACK];

    /* tempo bonus */
    if ( b.stm == WHITE )
        result += e.TEMPO;
    else
        result -= e.TEMPO;

     /* add in pawn structure evaluation */
     result += getPawnScore();

     /*******************************************************************
     *  Low material correction - guarding against an illusory material *
     *  advantage.  Program  will not not  expect to  win  having  only *
     *  a single minor piece and no pawns.                              *
     *******************************************************************/

     if ( ( p.PawnMaterial[WHITE] == 0 )   &&
          ( p.PieceMaterial[WHITE] < 400 ) &&
          ( result > 0 ) )
           return 0;

     if ( ( p.PawnMaterial[BLACK] == 0 )   &&
          ( p.PieceMaterial[BLACK] < 400 ) &&
          ( result < 0 ) )
           return 0;

     /*******************************************************************
     *  Program will not expect to win having only two knights in case  *
     *  neither  side  has pawns. Please note that this  code  assumes  *
     *  different values for bishop and knight, and eval_init() should  *
     *  take care of that.                                              *
     *******************************************************************/

     if ( !p.PawnMaterial[WHITE] && !p.PawnMaterial[BLACK] ) {
          if ( p.PieceMaterial[WHITE] == 2 * e.PIECE_VALUE[KNIGHT] && result > 0 )
             result = 0;
          if ( p.PieceMaterial[BLACK] == 2 * e.PIECE_VALUE[KNIGHT] && result < 0 )
              result = 0;
     }

    /*******************************************************************
    * Adjusting material value for the various combinations of pieces. *
    * Currently it scores bishop, knight and rook pairs. The first one *
    * gets a bonus, the latter two - a penalty. Please also note that  *
    * adjustements of knight and rook value based on the number of own *
    * pawns on the board are done within the piece-specific routines.  *
    *******************************************************************/

     if ( p.PieceCount[WHITE][BISHOP] > 1 ) v.MaterialAdjustement[WHITE] += e.BISHOP_PAIR;
     if ( p.PieceCount[BLACK][BISHOP] > 1 ) v.MaterialAdjustement[BLACK] += e.BISHOP_PAIR;
     if ( p.PieceCount[WHITE][KNIGHT] > 1 ) v.MaterialAdjustement[WHITE] -= e.P_KNIGHT_PAIR;
     if ( p.PieceCount[BLACK][KNIGHT] > 1 ) v.MaterialAdjustement[BLACK] -= e.P_KNIGHT_PAIR;
     if ( p.PieceCount[WHITE]  [ROOK] > 1 ) v.MaterialAdjustement[WHITE] -= e.P_ROOK_PAIR;
     if ( p.PieceCount[BLACK]  [ROOK] > 1 ) v.MaterialAdjustement[BLACK] -= e.P_ROOK_PAIR;


     // penalty for the lack of pawns - added 28.07.2008
     if ( p.PieceCount[WHITE] [PAWN] == 0 ) v.MaterialAdjustement[WHITE] -= 16;
     if ( p.PieceCount[BLACK] [PAWN] == 0 ) v.MaterialAdjustement[BLACK] -= 16;

    /********************************************************************
    *   Evaluate piece placement. This giant loop calls piece-specific  *
    *   functions which tend to do four things:                         *
    *                                                                   *
    *   (1) they look for the trapped pieces and blockages              *
    *   (2) they look for rooks on (half) open files                    *
    *   (3) they calculate mobility and king safety                     *
    *   (4) they calculate adjustements of material value based on      *
    *      the number of pawns                                          *
    ********************************************************************/

    for (int i=0; i < pieceIndex; i++) {

      S8 sq = pieceList[i];

      switch ( b.color[sq] ) {
           case WHITE : {
               switch ( b.pieces[sq] ) {
                 case KNIGHT : wKnightEval(sq); break;
                 case BISHOP : wBishopEval(sq); break;
                 case ROOK   : wRookEval(sq);   break;
                 case QUEEN  : wQueenEval(sq);  break;
               }
           }
           break;
           case BLACK : {
               switch ( b.pieces[sq] ) {
                 case KNIGHT : bKnightEval(sq); break;
                 case BISHOP : bBishopEval(sq); break;
                 case ROOK   : bRookEval(sq);   break;
                 case QUEEN  : bQueenEval(sq);  break;
               }
           }
           break;
        }
    }

    /********************************************************************
    *   After  the piece evaluation loop we have the king tropism  data *
    *   in order, so it is time to do full king evaluation. For details *
    *   see comments in wKingEval() function.                           *
    ********************************************************************/

    result += wKingEval( p.KingLoc[WHITE] );
    result -= bKingEval( p.KingLoc[BLACK] );

    /********************************************************************
    *   Pattern evaluation - mainly things interrelated with the pawn   *
    *   position, not fitting elsewhere.                                *
    ********************************************************************/

    blockedCentralPawns(); // don't block central pawns on initial squares
    blockedRooks();        // avoid pseudo-castling which blocks the rook
    slavMistake();         // don't play c4-c5 against Slav / Stonewall
    evalFianchetto();

    /********************************************************************
    *  Fold in data gathered in evaluation vector.                      *
    ********************************************************************/

    result += v.MaterialAdjustement[WHITE];
    result -= v.MaterialAdjustement[BLACK];
    result += v.Blockages[WHITE];
    result -= v.Blockages[BLACK];
    result += v.PositionalThemes[WHITE];
    result -= v.PositionalThemes[BLACK];

    /******************************************************************
    *  Here mobility score is scaled according to the side to move.   *
    *  We  give  more weight to opponent's mobility  to  encourage    *
    *  playing for restraint.                                         *
    ******************************************************************/

    if ( sd.myside == WHITE ) {
        v.Mobility[BLACK] *= 4;
        v.Mobility[BLACK] /= 3;
    } else {
        v.Mobility[WHITE] *= 4;
        v.Mobility[WHITE] /= 3;
    }

    result += v.Mobility[WHITE];
    result -= v.Mobility[BLACK];

    /*******************************************************************
    *  Finally return the score relative to the side to move.          *
    *******************************************************************/

    if ( b.stm == BLACK )
       result = -result;

    // save value in the eval tt
    tteval_save(result);

    return result;
}

/***********************************************************************
*                     2. Preparatory routines                          *
***********************************************************************/

void eval_setPieceLists() {

/***********************************************************************
* Create  local  lists  of pieces and pawns. This is  done  to  avoid  *
* looping through the entire board three times: for pawns, for pieces  *
* and again evaluating mobility if lazy eval does not produce a cutoff.*
***********************************************************************/

   pieceIndex = 0;
   pawnIndex  = 0;

   for (U8 row=0; row < 8; row++)
   for (U8 col=0; col < 8; col++) {

      S8 sq = row * 16 + col;

      if ( b.color[sq] != COLOR_EMPTY ) {
         if ( b.pieces[sq] == PAWN ) {
             pawnList[pawnIndex] = sq;
             ++pawnIndex;
         }
         else {
             pieceList[pieceIndex] = sq;
             ++pieceIndex;
          }
      }
   }
}

/***********************************************************************
* This is a reduced version of the previous function, and it creates   *
* only a list of pawns.                                                *
***********************************************************************/

void eval_setPawnLists() {

    pawnIndex  = 0;

    for ( U8 row=0; row < 8; row++ )
    for ( U8 col=0; col < 8; col++ ) {

      S8 sq = row * 16 + col;

      if ( b.color[sq] != COLOR_EMPTY &&
           b.pieces[sq] == PAWN ) {
             pawnList[pawnIndex] = sq;
             ++pawnIndex;
         }
    }
}


void eval_clearVector() {
    v.MaterialAdjustement[WHITE] = 0;
    v.MaterialAdjustement[BLACK] = 0;
    v.PositionalThemes[WHITE]    = 0;
    v.PositionalThemes[BLACK]    = 0;
    v.KingAttackers[WHITE] = 0;
    v.KingAttackers[BLACK] = 0;
    v.KingPressure[WHITE]  = 0;
    v.KingPressure[BLACK]  = 0;
    v.Blockages[WHITE]     = 0;
    v.Blockages[BLACK]     = 0;
    v.Mobility[WHITE]      = 0;
    v.Mobility[BLACK]      = 0;
}

/************************************************************************
*                    3. King safety evaluation                          *
************************************************************************/

int wKingEval(S8 sq) {
    int result = 0;

    if ( p.PieceMaterial[WHITE] < e.ENDGAME_MAT ) {
        result += e.endgame_king[sq];
    } else {
        result += e.PIECESQUARE[KING][WHITE][sq];
        result += wKingShield();
        result -= scaleAttacks( v.KingPressure[BLACK], v.KingAttackers[BLACK] );

        /* Scale the middlegame king evaluation against remaining enemy material */
        result *= p.PieceMaterial[BLACK];
        result /= e.START_MATERIAL;
    }

   return result;
}

int bKingEval(S8 sq) {
   int result = 0;

   if ( p.PieceMaterial[BLACK] < e.ENDGAME_MAT ) {
        result += e.endgame_king[sq];
   } else {
        result += e.PIECESQUARE[KING][BLACK][sq];
        result += bKingShield();
        result -= scaleAttacks( v.KingPressure[WHITE], v.KingAttackers[WHITE] );

        /* Scale the middlegame king evaluation against remaining enemy material */
        result *= p.PieceMaterial[WHITE];
        result /= e.START_MATERIAL;
   }

   return result;
}

int scaleAttacks(int attack_value, int n_of_attackers) {
    int result;

    switch ( n_of_attackers ) {
               case 0 : result = 0;
               case 1 : result = 0;
               case 2 : result = attack_value;
               case 3 : result = ( attack_value * 4 ) / 3;
               case 4 : result = ( attack_value * 3 ) / 2;
               default: result = attack_value * 2;
        }

    return result;
}

int wKingShield() {

    int result = 0;

    /* king on the kingside */
    if ( COL(p.KingLoc[WHITE]) > COL_E ) {

       if ( isPiece(WHITE, PAWN, F2) )  result += e.SHIELD_1;
       else if ( isPiece(WHITE, PAWN, F3) )  result += e.SHIELD_2;

       if ( isPiece(WHITE, PAWN, G2) )  result += e.SHIELD_1;
       else if ( isPiece(WHITE, PAWN, G3) )  result += e.SHIELD_2;
       else if ( p.PawnsOnFile [WHITE] [ COL_G ] == 0 ) result -= e.P_NO_SHIELD;

       if ( isPiece(WHITE, PAWN, H2) )  result += e.SHIELD_1;
       else if ( isPiece(WHITE, PAWN, H3) )  result += e.SHIELD_2;
       else if ( p.PawnsOnFile [WHITE] [ COL_H ] == 0 ) result -= e.P_NO_SHIELD;
   }

   /* king on the queenside */
   else if ( COL(p.KingLoc[WHITE]) < COL_D ) {

       if ( isPiece(WHITE, PAWN, A2) )  result += e.SHIELD_1;
       else if ( isPiece(WHITE, PAWN, A3) )  result += e.SHIELD_2;
       else if ( p.PawnsOnFile [WHITE] [ COL_A ] == 0 ) result -= e.P_NO_SHIELD;

       if ( isPiece(WHITE, PAWN, B2) )  result += e.SHIELD_1;
       else if ( isPiece(WHITE, PAWN, B3) )  result += e.SHIELD_2;
       else if ( p.PawnsOnFile [WHITE] [ COL_B ] == 0 ) result -= e.P_NO_SHIELD;

       if ( isPiece(WHITE, PAWN, C2) )  result += e.SHIELD_1;
       else if ( isPiece(WHITE, PAWN, C3) )  result += e.SHIELD_2;
   }

return result;
}

int bKingShield() {
    int result = 0;

    /* king on the kingside */
    if ( COL(p.KingLoc[BLACK]) > COL_E ) {
        if ( isPiece(BLACK, PAWN, F7) )  result += e.SHIELD_1;
        else if ( isPiece(BLACK, PAWN, F6) )  result += e.SHIELD_2;

        if ( isPiece(BLACK, PAWN, G7) )  result += e.SHIELD_1;
        else if ( isPiece(BLACK, PAWN, G6) )  result += e.SHIELD_2;
        else if ( p.PawnsOnFile [BLACK] [ COL_G ] == 0 ) result -= e.P_NO_SHIELD;

        if ( isPiece(BLACK, PAWN, H7) )  result += e.SHIELD_1;
        else if ( isPiece(BLACK, PAWN, H6) )  result += e.SHIELD_2;
        else if ( p.PawnsOnFile [BLACK] [ COL_H ] == 0 ) result -= e.P_NO_SHIELD;
   }

   /* king on the queenside */
   else if ( COL(p.KingLoc[BLACK]) < COL_D ) {
       if ( isPiece(BLACK, PAWN, A7) )  result += e.SHIELD_1;
       else if ( isPiece(BLACK, PAWN, A6) )  result += e.SHIELD_2;
       else if ( p.PawnsOnFile [BLACK] [ COL_A ] == 0 ) result -= e.P_NO_SHIELD;

       if ( isPiece(BLACK, PAWN, B7) )  result += e.SHIELD_1;
       else if ( isPiece(BLACK, PAWN, B6) )  result += e.SHIELD_2;
       else if ( p.PawnsOnFile [BLACK] [ COL_B ] == 0 ) result -= e.P_NO_SHIELD;

       if ( isPiece(BLACK, PAWN, C7) )  result += e.SHIELD_1;
       else if ( isPiece(BLACK, PAWN, C6) )  result += e.SHIELD_2;
  }
  return result;
}

/*********************************************************************************
*                            4. Pawn structure evaluaton                         *
*********************************************************************************/

int getPawnScore() {
    int result;

    /*****************************************************************************
    *   This function wraps hashing mechanism around evalPawnStructure(). Please *
    *   note  that since we use the pawn hashtable, evalPawnStructure() must not *
    *   take into account the piece position.  In a more elaborate program, pawn *
    *   hashtable would contain only the characteristics of pawn structure,  and *
    *   scoring them in conjunction with the piece position would have been done *
    *   elsewhere.                                                               *
    ******************************************************************************/

    int probeval = ttpawn_probe();
    if (probeval != INVALID)
        return probeval;

    result = evalPawnStructure();

    ttpawn_save(result);

    return result;
}

/* a clone of getPawnScore, used in fastEval, when the pawn list is not set */

int getPawnScoreNoList() {
    int result;

    int probeval = ttpawn_probe();
    if (probeval != INVALID)
        return probeval;

    eval_setPawnLists();
    result = evalPawnStructure();

    ttpawn_save(result);

    return result;
}

int evalPawnStructure() {
    int result = 0;

    /* 1. evaluate pawn center */
    result += evalPawnCenter();

    /* 2. evaluate doubled/tripled pawns */
    for (U8 col=0; col < 8; col++) {
        result -= e.P_MULTI_PAWN[ p.PawnsOnFile [WHITE] [col] ];
        result += e.P_MULTI_PAWN[ p.PawnsOnFile [BLACK] [col] ];
    }

   /* 3. core procedure: loop through pawn list, evaluating each pawn on the board */
   for (U8 i=0; i < pawnIndex; i++) {

       S8 sq = pawnList[i];

       if (b.color[sq] == WHITE)
          result += wPawnEval(sq);
       else
          result -= bPawnEval(sq);
   }
   return result;
}

int evalPawnCenter() {
    int result = 0;

    if ( isPiece(WHITE, PAWN, D4) ) {
        if (isPiece(WHITE, PAWN, E4 ) ) result += e.DUO_D4E4;
        if (isPiece(WHITE, PAWN, C4 ) ) result += e.DUO_D4C4;
        if (isPiece(WHITE, PAWN, E3 ) ) result += e.DUO_D4E3;
    }

    if ( isPiece(WHITE, PAWN, E4) ) {
        if (isPiece(WHITE, PAWN, F4 ) ) result += e.DUO_E4F4;
        if (isPiece(WHITE, PAWN, D3 ) ) result += e.DUO_E4D3;
    }

    if ( isPiece(BLACK, PAWN, D5) ) {
        if (isPiece(BLACK, PAWN, E5 ) ) result -= e.DUO_D4E4;
        if (isPiece(BLACK, PAWN, C5 ) ) result -= e.DUO_D4C4;
        if (isPiece(BLACK, PAWN, E6 ) ) result -= e.DUO_D4E3;
    }

    if ( isPiece(BLACK, PAWN, E5) ) {
        if (isPiece(BLACK, PAWN, F5 ) ) result -= e.DUO_E4F4;
        if (isPiece(BLACK, PAWN, D6 ) ) result -= e.DUO_E4D3;
    }

    return result;
}

int wPawnEval(S8 sq) {
    int result = 0;

    /* 1. Evaluate passed pawns, scoring them higher if they are
          protected by friendly pawns */

    if ( isWPFree(sq) ) {
        if (isWPSupported(sq) )
            result += e.w_protected_passer[sq];
        else
            result += e.w_passed_pawn[sq];
    }

    /* 2. Evaluate weak pawns */

    if ( isWPWeak(sq) ) {
        result += e.w_weak_pawn[sq];
        /* weak pawns on half-open files tend to be even weaker */
        if ( p.PawnsOnFile[BLACK] [COL( sq) ] == 0 )
            result -= 4;
    }

    return result;
}

int bPawnEval(S8 sq) {
    int result = 0;

    /* 1. Evaluate passed pawns, scoring them higher if they are
          protected by friendly pawns */

    if ( isBPFree(sq) ) {
        if ( isBPSupported(sq) )
            result += e.b_protected_passer[sq];
        else
            result += e.b_passed_pawn[sq];
    }

    /* 2. Evaluate weak pawns */

    if ( isBPWeak(sq) ) {
        result += e.b_weak_pawn[sq];
        /* weak pawns on half-open files tend to be even weaker */
        if ( p.PawnsOnFile[WHITE] [COL( sq) ] == 0 )
            result -= 4;
    }

    return result;
}

int isWPFree(S8 sq) {

    S8 nextSq = sq + NORTH;

    while ( IS_SQ(nextSq) ) {

       /* either blocked by enemy pawn or doubled */
       if ( b.pieces[nextSq] == PAWN )
          return 0;

       if ( IS_SQ(nextSq + WEST) && isPiece(BLACK, PAWN, nextSq + WEST) )
          return 0;

       if ( IS_SQ(nextSq + EAST) && isPiece(BLACK, PAWN, nextSq + EAST) )
           return 0;

       nextSq += NORTH;
    }

    return 1;
}

int isBPFree(S8 sq) {
    S8 nextSq = sq + SOUTH;

    while ( IS_SQ(nextSq) ) {

       /* either blocked by enemy pawn or doubled */
       if ( b.pieces[nextSq] == PAWN )
          return 0;

       if ( IS_SQ(nextSq + WEST) && isPiece(WHITE, PAWN, nextSq + WEST) )
          return 0;

       if ( IS_SQ(nextSq + EAST) && isPiece(WHITE, PAWN, nextSq + EAST) )
           return 0;

       nextSq += SOUTH;
    }

    return 1;
}

int isWPWeak(S8 sq) {
    S8 nextSq = sq;

    while ( IS_SQ(nextSq) ) {

       if ( IS_SQ(nextSq + WEST) && isPiece(WHITE, PAWN, nextSq + WEST) )
          return 0;

       if ( IS_SQ(nextSq + EAST) && isPiece(WHITE, PAWN, nextSq + EAST) )
           return 0;

       nextSq += SOUTH;
    }

    return 1;
}

int isBPWeak(S8 sq) {
    S8 nextSq = sq;

    while ( IS_SQ(nextSq) ) {

       if ( IS_SQ(nextSq + WEST) && isPiece(BLACK, PAWN, nextSq + WEST) )
          return 0;

       if ( IS_SQ(nextSq + EAST) && isPiece(BLACK, PAWN, nextSq + EAST) )
           return 0;

       nextSq += NORTH;
    }

    return 1;
}

/****************************************************************
*  The next two procedures are used in passed pawn evaluation,  *
*  the assumption being that passed pawns that are defended or  *
*  or whose stop square is protected by a friendly pawn tend    *
*  to be stronger.                                              *
*****************************************************************/

int isWPSupported(S8 sq) {
    if ( IS_SQ(sq+WEST) && isPiece(WHITE,PAWN, sq + WEST) ) return 1;
    if ( IS_SQ(sq+EAST) && isPiece(WHITE,PAWN, sq + EAST) ) return 1;
    if ( IS_SQ( sq+SE ) && isPiece(WHITE,PAWN, sq + SE  ) ) return 1;
    if ( IS_SQ( sq+SW ) && isPiece(WHITE,PAWN, sq + SW  ) ) return 1;

    return 0;
}

int isBPSupported(S8 sq) {
    if ( IS_SQ(sq+WEST) && isPiece(BLACK,PAWN, sq + WEST) ) return 1;
    if ( IS_SQ(sq+EAST) && isPiece(BLACK,PAWN, sq + EAST) ) return 1;
    if ( IS_SQ( sq+NE ) && isPiece(BLACK,PAWN, sq + NE  ) ) return 1;
    if ( IS_SQ( sq+NW ) && isPiece(BLACK,PAWN, sq + NW  ) ) return 1;

    return 0;
}

/************************************************************************************
*                        5. Evaluation of pieces                                    *
************************************************************************************/

void wKnightEval(S8 sq) {

    /* material value adjustement based on the no. of own pawns */
    v.MaterialAdjustement[WHITE] += knight_adj[ p.PieceCount[WHITE] [PAWN] ];

    /* mobility and king attacks calculation */
    v.Mobility[WHITE] += wKnightMob(sq);

    /* trapped or blocking knight */
    switch (sq) {
      case A8 : if (isPiece(BLACK, PAWN, A7) || isPiece(BLACK, PAWN, C7) ) v.Blockages[WHITE] -= e.P_KNIGHT_TRAPPED_A8; break;
      case H8 : if (isPiece(BLACK, PAWN, H7) || isPiece(BLACK, PAWN, F7) ) v.Blockages[WHITE] -= e.P_KNIGHT_TRAPPED_A8; break;
      case A7 : if (isPiece(BLACK, PAWN, A6) && isPiece(BLACK, PAWN, B7) ) v.Blockages[WHITE] -= e.P_KNIGHT_TRAPPED_A7; break;
      case H7 : if (isPiece(BLACK, PAWN, H6) && isPiece(BLACK, PAWN, G7) ) v.Blockages[WHITE] -= e.P_KNIGHT_TRAPPED_A7; break;
      case C3 : if (isPiece(WHITE, PAWN, C2) && isPiece(WHITE, PAWN, D4) && !isPiece(WHITE, PAWN, E4) ) v.Blockages[WHITE] -= e.P_C3_KNIGHT; break;
    }
}

void bKnightEval(S8 sq) {

    /* material value adjustement based on the no. of own pawns */
    v.MaterialAdjustement[BLACK] += knight_adj[ p.PieceCount[BLACK] [PAWN] ];

    /* mobility and king attack calculation */
    v.Mobility[BLACK] += bKnightMob(sq);

    /* trapped or blocking knight */
    switch (sq) {
      case A1 : if (isPiece(WHITE, PAWN, A2) || isPiece(WHITE, PAWN, C2) ) v.Blockages[BLACK] -= e.P_KNIGHT_TRAPPED_A8; break;
      case H1 : if (isPiece(WHITE, PAWN, H2) || isPiece(WHITE, PAWN, F2) ) v.Blockages[BLACK] -= e.P_KNIGHT_TRAPPED_A8; break;
      case A2 : if (isPiece(WHITE, PAWN, A3) && isPiece(WHITE, PAWN, B2) ) v.Blockages[BLACK] -= e.P_KNIGHT_TRAPPED_A7; break;
      case H2 : if (isPiece(WHITE, PAWN, H3) && isPiece(WHITE, PAWN, G2) ) v.Blockages[BLACK] -= e.P_KNIGHT_TRAPPED_A7; break;
      case C6 : if (isPiece(BLACK, PAWN, C7) && isPiece(BLACK, PAWN, D5) && !isPiece(BLACK, PAWN, E5) ) v.Blockages[BLACK] -= e.P_C3_KNIGHT; break;
    }
}

void wBishopEval(S8 sq) {

    /* mobility and king attack calculaion */
    v.Mobility[WHITE] += wBishopMob(sq);

    /* trapped bishop and returning bishop */
    switch (sq) {
      case A7 : if ( isPiece(BLACK, PAWN, B6) ) v.Blockages[WHITE] -= e.P_BISHOP_TRAPPED_A7; break;
      case H7 : if ( isPiece(BLACK, PAWN, G6) ) v.Blockages[WHITE] -= e.P_BISHOP_TRAPPED_A7; break;
      case B8 : if ( isPiece(BLACK, PAWN, C7) ) v.Blockages[WHITE] -= e.P_BISHOP_TRAPPED_A7; break;
      case G8 : if ( isPiece(BLACK, PAWN, F7) ) v.Blockages[WHITE] -= e.P_BISHOP_TRAPPED_A7; break;
      case A6 : if ( isPiece(BLACK, PAWN, B5) ) v.Blockages[WHITE] -= e.P_BISHOP_TRAPPED_A6; break;
      case H6 : if ( isPiece(BLACK, PAWN, G5) ) v.Blockages[WHITE] -= e.P_BISHOP_TRAPPED_A6; break;
      case F1 : if ( isPiece(WHITE, KING, G1) ) v.PositionalThemes[WHITE] += e.RETURNING_BISHOP; break;
      case C1 : if ( isPiece(WHITE, KING, B1) ) v.PositionalThemes[WHITE] += e.RETURNING_BISHOP; break;
    }
}

void bBishopEval(S8 sq) {

    /* mobility and king attack calculation */
    v.Mobility[BLACK] += bBishopMob(sq);

    /* trapped bishop and returning bishop */
    switch (sq) {
      case A2 : if ( isPiece(WHITE, PAWN, B3) ) v.Blockages[BLACK] -= e.P_BISHOP_TRAPPED_A7; break;
      case H2 : if ( isPiece(WHITE, PAWN, G3) ) v.Blockages[BLACK] -= e.P_BISHOP_TRAPPED_A7; break;
      case B1 : if ( isPiece(WHITE, PAWN, C2) ) v.Blockages[BLACK] -= e.P_BISHOP_TRAPPED_A7; break;
      case G1 : if ( isPiece(WHITE, PAWN, F2) ) v.Blockages[BLACK] -= e.P_BISHOP_TRAPPED_A7; break;
      case A3 : if ( isPiece(WHITE, PAWN, B4) ) v.Blockages[BLACK] -= e.P_BISHOP_TRAPPED_A6; break;
      case H3 : if ( isPiece(WHITE, PAWN, G4) ) v.Blockages[BLACK] -= e.P_BISHOP_TRAPPED_A6; break;
      case F8 : if ( isPiece(BLACK, KING, G8) ) v.PositionalThemes[BLACK] += e.RETURNING_BISHOP; break;
      case C8 : if ( isPiece(BLACK, KING, B8) ) v.PositionalThemes[BLACK] += e.RETURNING_BISHOP; break;
    }
}

void wRookEval(S8 sq) {

    /* material value adjustement based on the no. of own pawns */
    v.MaterialAdjustement[WHITE] += rook_adj[ p.PieceCount[WHITE] [PAWN] ];

    /* mobility and king attack calculation */
    v.Mobility[WHITE] += wRookMob(sq);

    /* open and half-open files */
    if ( p.PawnsOnFile [WHITE] [ COL(sq) ] == 0 ) {

       if ( p.PawnsOnFile [BLACK] [ COL(sq) ] == 0 )
           v.PositionalThemes[WHITE] += e.ROOK_OPEN;
       else
           v.PositionalThemes[WHITE] += e.ROOK_HALF;
    }
}

void bRookEval(S8 sq) {

    /* material value adjustement based on the no. of own pawns */
    v.MaterialAdjustement[BLACK] += rook_adj[ p.PieceCount[BLACK] [PAWN] ];

    /* mobility and king attack calculation */
    v.Mobility[BLACK] += bRookMob(sq);

    /* open and half-open files */
    if ( p.PawnsOnFile [BLACK] [ COL(sq) ] == 0 ) {

       if ( p.PawnsOnFile [WHITE] [ COL(sq) ] == 0 )
           v.PositionalThemes[BLACK] += e.ROOK_OPEN;
       else
           v.PositionalThemes[BLACK] += e.ROOK_HALF;
    }
}

void wQueenEval(S8 sq) {

    /* mobility and king attack calculation */
    v.Mobility[WHITE] += wQueenMob(sq);

    /* penalize premature developement */
    if ( ROW(sq) > ROW_2 ) {
        if ( isPiece(WHITE, KNIGHT, B1) ) v.PositionalThemes[WHITE] -= 2;
        if ( isPiece(WHITE, BISHOP, C1) ) v.PositionalThemes[WHITE] -= 2;
        if ( isPiece(WHITE, BISHOP, F1) ) v.PositionalThemes[WHITE] -= 2;
        if ( isPiece(WHITE, KNIGHT, G1) ) v.PositionalThemes[WHITE] -= 2;
    }
}

void bQueenEval(S8 sq) {

    /* mobility and king attack calculation */
    v.Mobility[BLACK] += bQueenMob(sq);

    /* penalize premature developement */
    if ( ROW(sq) < ROW_7 ) {
        if ( isPiece(BLACK, KNIGHT, B8) ) v.PositionalThemes[BLACK] -= 2;
        if ( isPiece(BLACK, BISHOP, C8) ) v.PositionalThemes[BLACK] -= 2;
        if ( isPiece(BLACK, BISHOP, F8) ) v.PositionalThemes[BLACK] -= 2;
        if ( isPiece(BLACK, KNIGHT, G8) ) v.PositionalThemes[BLACK] -= 2;
    }
}

/**********************************************************************************
*                        6. Mobility and king attack evaluation                   *
**********************************************************************************/

int wKnightMob(S8 sq) {

    isAttacker = 0;
    int localMobility = leaperMobility(WHITE, sq, KNIGHT, MINOR_ATT);
    if ( isAttacker )
        ++v.KingAttackers[WHITE];

    return knight_mob[localMobility];
}

int bKnightMob(S8 sq) {

    isAttacker = 0;
    int localMobility = leaperMobility(BLACK, sq, KNIGHT, MINOR_ATT);
    if ( isAttacker )
        ++v.KingAttackers[BLACK];

    return knight_mob[localMobility];
}

int wBishopMob(S8 sq) {

    isAttacker = 0;

    int localMobility = sliderMobility(WHITE, sq, NE, MINOR_ATT )
                        + sliderMobility(WHITE, sq, NW, MINOR_ATT )
                        + sliderMobility(WHITE, sq, SE, MINOR_ATT )
                        + sliderMobility(WHITE, sq, SW, MINOR_ATT );

    if ( isAttacker )
        ++v.KingAttackers[WHITE];

    return bish_mob[localMobility];
}

int bBishopMob(S8 sq) {

    isAttacker = 0;

    int localMobility = sliderMobility(BLACK, sq, NE, MINOR_ATT )
                      + sliderMobility(BLACK, sq, NW, MINOR_ATT )
                      + sliderMobility(BLACK, sq, SE, MINOR_ATT )
                      + sliderMobility(BLACK, sq, SW, MINOR_ATT );

    if ( isAttacker )
        ++v.KingAttackers[BLACK];

    return bish_mob[localMobility];
}

int wRookMob(S8 sq) {

    isAttacker = 0;

    int localMobility = sliderMobility(WHITE, sq, NORTH, ROOK_ATT )
                      + sliderMobility(WHITE, sq, SOUTH, ROOK_ATT )
                      + sliderMobility(WHITE, sq, EAST,  ROOK_ATT )
                      + sliderMobility(WHITE, sq, WEST,  ROOK_ATT );

    if ( isAttacker )
        ++v.KingAttackers[WHITE];

    return rook_mob[localMobility];
}

int bRookMob(S8 sq) {

    isAttacker = 0;

    int localMobility = sliderMobility(BLACK, sq, NORTH, ROOK_ATT )
                      + sliderMobility(BLACK, sq, SOUTH, ROOK_ATT )
                      + sliderMobility(BLACK, sq, EAST,  ROOK_ATT )
                      + sliderMobility(BLACK, sq, WEST,  ROOK_ATT );

    if ( isAttacker )
        ++v.KingAttackers[BLACK];

    return rook_mob[localMobility];
}

// with queen, currently we look for king attacks, not for mobility

int wQueenMob(S8 sq) {

    isAttacker = 0;

    int localMobility = sliderMobility(WHITE, sq, NORTH, QUEEN_ATT )
                      + sliderMobility(WHITE, sq, SOUTH, QUEEN_ATT )
                      + sliderMobility(WHITE, sq, EAST,  QUEEN_ATT )
                      + sliderMobility(WHITE, sq, WEST,  QUEEN_ATT )
                      + sliderMobility(WHITE, sq, NE,  QUEEN_ATT )
                      + sliderMobility(WHITE, sq, NW,  QUEEN_ATT )
                      + sliderMobility(WHITE, sq, SE,  QUEEN_ATT )
                      + sliderMobility(WHITE, sq, SW,  QUEEN_ATT );

    if ( isAttacker )
        ++v.KingAttackers[WHITE];

    return 0;
}

int bQueenMob(S8 sq) {

    isAttacker = 0;

    int localMobility = sliderMobility(BLACK, sq, NORTH, QUEEN_ATT )
                      + sliderMobility(BLACK, sq, SOUTH, QUEEN_ATT )
                      + sliderMobility(BLACK, sq, EAST,  QUEEN_ATT )
                      + sliderMobility(BLACK, sq, WEST,  QUEEN_ATT )
                      + sliderMobility(BLACK, sq, NE,  QUEEN_ATT )
                      + sliderMobility(BLACK, sq, NW,  QUEEN_ATT )
                      + sliderMobility(BLACK, sq, SE,  QUEEN_ATT )
                      + sliderMobility(BLACK, sq, SW,  QUEEN_ATT );

    if ( isAttacker )
        ++v.KingAttackers[BLACK];

    return 0;
}

int sliderMobility(U8 color, S8 sq, int vect, int attBonus) {
    int nextSq = sq + vect;
    int result = 0;

    while ( IS_SQ(nextSq) ) {

    if ( e.sqNearK[!color] [p.KingLoc[!color] ] [nextSq] ) {
       isAttacker = 1;
       v.KingPressure[color] += attBonus;
       }

       if (b.color[nextSq] != COLOR_EMPTY ) {
              if ( b.color[nextSq] != color )
              return result + 1;
           return result;
          }

       ++result;

       nextSq = nextSq + vect;
    }

    return result;
}

int leaperMobility(U8 color, S8 sq, char byPiece, int attBonus) {
    S8 nextSq;
    int result = 0;

    for (U8 dir=0;dir<8;dir++) {
        nextSq = sq + vector[byPiece][dir];

        if ( IS_SQ(nextSq) && b.color[nextSq] != color ) {

           /* king attack */
           if ( e.sqNearK[!color] [p.KingLoc[!color] ] [nextSq] ) {
              isAttacker = 1;
              v.KingPressure[color] += attBonus;
           }
           ++result;
        }
    }

    return result;
}

/***************************************************************************************
*                          7. Pattern detection                                        *
***************************************************************************************/

void blockedCentralPawns() {
     if ( isPiece(WHITE,PAWN,D2) && b.color[D3] != COLOR_EMPTY )
        v.Blockages[WHITE] -= e.P_BLOCK_CENTRAL_PAWN;
     if ( isPiece(WHITE,PAWN,E2) && b.color[E3] != COLOR_EMPTY )
        v.Blockages[WHITE] -= e.P_BLOCK_CENTRAL_PAWN;
     if ( isPiece(BLACK,PAWN,D7) && b.color[D6] != COLOR_EMPTY )
        v.Blockages[BLACK] -= e.P_BLOCK_CENTRAL_PAWN;
     if ( isPiece(BLACK,PAWN,E7) && b.color[E6] != COLOR_EMPTY )
        v.Blockages[BLACK] -= e.P_BLOCK_CENTRAL_PAWN;
}

void blockedRooks() {

    if ( ( isPiece(WHITE, KING, F1) || isPiece(WHITE, KING, G1 ) )&&
         ( isPiece(WHITE, ROOK, H1) || isPiece(WHITE, ROOK, G1 ) )
       )
       v.Blockages[WHITE] -= e.P_KING_BLOCKS_ROOK;

    if ( ( isPiece(WHITE, KING, C1) || isPiece(WHITE, KING, B1 ) )&&
         ( isPiece(WHITE, ROOK, A1) || isPiece(WHITE, ROOK, B1 ) )
       )
       v.Blockages[WHITE] -= e.P_KING_BLOCKS_ROOK;

    if ( ( isPiece(BLACK, KING, F8) || isPiece(BLACK, KING, G8 ) )&&
         ( isPiece(BLACK, ROOK, H8) || isPiece(BLACK, ROOK, G8 ) )
       )
       v.Blockages[BLACK] -= e.P_KING_BLOCKS_ROOK;

    if ( ( isPiece(BLACK, KING, C8) || isPiece(BLACK, KING, B8 ) )&&
         ( isPiece(BLACK, ROOK, A8) || isPiece(BLACK, ROOK, B8 ) )
       )
       v.Blockages[BLACK] -= e.P_KING_BLOCKS_ROOK;
}

void slavMistake() {
     if ( isPiece(WHITE, PAWN, D4) &&
          isPiece(WHITE, PAWN, C5) &&
          isPiece(BLACK, PAWN, D5) &&
          isPiece(BLACK, PAWN, C6) )
        v.PositionalThemes[WHITE] -= e.P_SLAV_MISTAKE;

     if ( isPiece(BLACK, PAWN, D5) &&
          isPiece(BLACK, PAWN, C4) &&
          isPiece(WHITE, PAWN, D4) &&
          isPiece(WHITE, PAWN, C3) )
        v.PositionalThemes[BLACK] -= e.P_SLAV_MISTAKE;
}

void evalFianchetto() {

    if ( isPiece(WHITE, PAWN, G3) ) {
       if ( isPiece(WHITE, BISHOP, G2 ) )
           v.PositionalThemes[WHITE] += e.FIANCHETTO;
       else {
           if ( !isPiece(WHITE, BISHOP, F3 ) &&
                !isPiece(WHITE, BISHOP, H1 ) &&
                !isPiece(WHITE, BISHOP, H3 ) )
           v.PositionalThemes[WHITE] -= e.P_NO_FIANCHETTO;
       }
    }

    if ( isPiece(WHITE, PAWN, B3) ) {
       if ( isPiece(WHITE, BISHOP, B2 ) )
           v.PositionalThemes[WHITE] += e.FIANCHETTO;
       else {
           if ( !isPiece(WHITE, BISHOP, C3 ) &&
                !isPiece(WHITE, BISHOP, A1 ) &&
                !isPiece(WHITE, BISHOP, A3 ) )

           v.PositionalThemes[WHITE] -= e.P_NO_FIANCHETTO;
       }
    }

   if ( isPiece(BLACK, PAWN, G6) ) {
       if ( isPiece(BLACK, BISHOP, G7 ) )
           v.PositionalThemes[BLACK] += e.FIANCHETTO;
       else {
              if ( !isPiece(BLACK, BISHOP, F6 ) &&
                !isPiece(BLACK, BISHOP, H8 ) &&
                !isPiece(BLACK, BISHOP, H6 ) )
           v.PositionalThemes[BLACK] -= e.P_NO_FIANCHETTO;
       }
    }

   if ( isPiece(BLACK, PAWN, B6) ) {
       if ( isPiece(BLACK, BISHOP, B7 ) )
           v.PositionalThemes[BLACK] += e.P_NO_FIANCHETTO;
       else {
           if ( !isPiece(BLACK, BISHOP, C6 ) &&
                !isPiece(BLACK, BISHOP, A8 ) &&
                !isPiece(BLACK, BISHOP, A6 ) )
           v.PositionalThemes[BLACK] -= e.P_NO_FIANCHETTO;
       }
    }
}

/* determine if two squares lie on the same or neighbouring columns */
int isNearCol(S8 sq1, S8 sq2) {
    U8 c1 = COL(sq1);
    U8 c2 = COL(sq2);
    U8 hor_dist = (U8) abs(c1 - c2);

    if ( hor_dist < 2 )
       return 1;
    else
       return 0;
}

int isPiece(U8 color, U8 piece, S8 sq) {
    return ( (b.pieces[sq] == piece) && (b.color[sq] == color) );
}

/***************************************************************************************
*                          8. Printing eval results                                    *
***************************************************************************************/

void printEval() {
    eval(-30000,30000);
    printf("------------------------------------------\n");
    printf("Total value (for side to move): %d \n", eval(-INFINITY,INFINITY) );
    printf("Material balance    : %d \n", p.PieceMaterial[WHITE] + p.PawnMaterial[WHITE] - p.PieceMaterial[BLACK] - p.PawnMaterial[BLACK] );
    printf("Material adjustement: %d \n", v.MaterialAdjustement[WHITE] - v.MaterialAdjustement[BLACK]);
    printf("Piece/square tables : %d \n", p.Pcsq[WHITE] - p.Pcsq[BLACK]);
    printf("Pawn structure      : %d \n", evalPawnStructure() );
    printf("Blockages           : %d \n", v.Blockages[WHITE] - v.Blockages[BLACK] );
    printf("Positional themes   : %d \n", v.PositionalThemes[WHITE] - v.PositionalThemes[BLACK] );
    printf("Mobility: white %d, black %d, total %d \n", v.Mobility[WHITE], v.Mobility[BLACK], v.Mobility[WHITE] - v.Mobility[BLACK] );
        printf("King pressure: white %d, black %d \n", v.KingPressure[WHITE], v.KingPressure[BLACK] );
    printf("King attackers: white %d, black %d \n", v.KingAttackers[WHITE], v.KingAttackers[BLACK] );
    printf("Kings: white %d , black %d, total: %d \n", wKingEval(p.KingLoc[WHITE]), bKingEval(p.KingLoc[BLACK] ), wKingEval(p.KingLoc[WHITE])-bKingEval(p.KingLoc[BLACK]) );
    printf("Tempo: ");
    if ( b.stm == WHITE ) printf("%d", e.TEMPO); else printf("%d", -e.TEMPO);
    printf("\n");
    printf("------------------------------------------\n");
}

Up one Level