/*
    Protector -- a UCI chess engine

    Copyright (C) 2009-2010 Raimund Heid (Raimund_Heid@yahoo.com)

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

*/

#include <assert.h>
#include <stdlib.h>
#include <math.h>
#include "position.h"
#include "fen.h"
#include "io.h"
#ifdef INCLUDE_TABLEBASE_ACCESS
#include "tablebase.h"
#endif

#define dumpPos dumpPosition(position);

#ifdef TEST_SETUP
/* #define KNIGHT_VS_PASSED_WING_PAWNS     /* */
/* #define BISHOP_BLOCKERS         /* */
/* #define PINNED_KNIGHT_BONUS     /* */
#define MALUS_ROOK_BLOCKING_PASSER      /* */
#define BONUS_SPACE_ATTACKS     /* */
/* #define MALUS_QUEEN_DEFENDER_DISTANCE   /* */
#define BONUS_OUTPOST_ATTACKS   /* */
#define BONUS_WINNING_PASSER    /* */
#define BONUS_PIECE_ATTACKS     /* */
#define BONUS_SLIDER_ATTACKS    /* */
#define MALUS_PAWN_MOBILITY     /* */
/* #define BONUS_PIECES_PROTECT_KING       /* */
#define MALUS_KING_TRAPPED      /* */
/* #define BONUS_OUTSIDE_PASSER    /* */
#define HOMELAND_SECURITY_BONUS /* */
/* #define MALUS_OFFSIDE_KING /* */
/* #define GENERATE_TABLES /* */
/* #define BONUS_PINS /* */
/* #define BONUS_ROOK_6TH_RANK     /* */
/* #define BONUS_ROOK_8TH_RANK     /* */
/* #define ATTACK_WEIGHT_ORIGINAL     /* */
#define INCLUDE_PASSERS_IN_FUTILITY_MARGIN      /* */
/* #define MALUS_BISHOP_BORDER     /* */
/* #define KAMIKAZE_LIMITATION     /* */
/* #define BONUS_EXCHANGE_UP       /* */
/* #define BONUS_HALF_OPEN_FILE_ROOK       /* */
/* #define EXTRA_BONUS_KING_FILE_ATTACKS   /* */
#define REDUCED_PAWN_EVAL       /* */
#define MOBILITY_TABLES         /* */
/* #define HIGH_ATTACK_BONUS       /* */
#define PST_TABLES              /* */
#else
/* #define KNIGHT_VS_PASSED_WING_PAWNS     /* */
/* #define BISHOP_BLOCKERS         /* */
/* #define PINNED_KNIGHT_BONUS     /* */
#define MALUS_ROOK_BLOCKING_PASSER      /* */
#define BONUS_SPACE_ATTACKS     /* */
/* #define MALUS_QUEEN_DEFENDER_DISTANCE   /* */
#define BONUS_OUTPOST_ATTACKS   /* */
#define BONUS_WINNING_PASSER    /* */
#define BONUS_PIECE_ATTACKS     /* */
#define BONUS_SLIDER_ATTACKS    /* */
#define MALUS_PAWN_MOBILITY     /* */
/* #define BONUS_PIECES_PROTECT_KING       /* */
#define MALUS_KING_TRAPPED      /* */
/* #define BONUS_OUTSIDE_PASSER    /* */
#define HOMELAND_SECURITY_BONUS /* */
/* #define MALUS_OFFSIDE_KING /* */
/* #define GENERATE_TABLES /* */
/* #define BONUS_PINS /* */
/* #define BONUS_ROOK_6TH_RANK     /* */
/* #define BONUS_ROOK_8TH_RANK     /* */
/* #define ATTACK_WEIGHT_ORIGINAL     /* */
#define INCLUDE_PASSERS_IN_FUTILITY_MARGIN      /* */
/* #define MALUS_BISHOP_BORDER     /* */
/* #define KAMIKAZE_LIMITATION     /* */
/* #define BONUS_EXCHANGE_UP       /* */
/* #define BONUS_HALF_OPEN_FILE_ROOK       /* */
/* #define EXTRA_BONUS_KING_FILE_ATTACKS   /* */
#define REDUCED_PAWN_EVAL       /* */
#define MOBILITY_TABLES         /* */
/* #define HIGH_ATTACK_BONUS       /* */
/* #define PST_TABLES              /* */
#endif

#include "evaluation.h"

const INT32 KnightMobilityBonusSF[16] = {
   V(-38, -33), V(-25, -23), V(-12, -13), V(0, -3),
   V(12, 7), V(25, 17), V(31, 22), V(38, 27), V(38, 27)
};

const INT32 BishopMobilityBonusSF[16] = {
   V(-25, -30), V(-11, -16), V(3, -2), V(17, 12),
   V(31, 26), V(45, 40), V(57, 52), V(65, 60),
   V(71, 65), V(74, 69), V(76, 71), V(78, 73),
   V(79, 74), V(80, 75), V(81, 76), V(81, 76)
};

const INT32 RookMobilityBonusSF[16] = {
   V(-20, -36), V(-14, -19), V(-8, -3), V(-2, 13),
   V(4, 29), V(10, 46), V(14, 62), V(19, 79),
   V(23, 95), V(26, 106), V(27, 111), V(28, 114),
   V(29, 116), V(30, 117), V(31, 118), V(32, 118)
};

const INT32 QueenMobilityBonusSF[32] = {
   V(-10, -18), V(-8, -13), V(-6, -7), V(-3, -2), V(-1, 3), V(1, 8),
   V(3, 13), V(5, 19), V(8, 23), V(10, 27), V(12, 32), V(15, 34),
   V(16, 35), V(17, 35), V(18, 35), V(20, 35), V(20, 35), V(20, 35),
   V(20, 35), V(20, 35), V(20, 35), V(20, 35), V(20, 35), V(20, 35),
   V(20, 35), V(20, 35), V(20, 35), V(20, 35), V(20, 35), V(20, 35),
   V(20, 35), V(20, 35)
};

#define MAX_MOVES_KNIGHT 8
#define MAX_MOVES_BISHOP 13
#define MAX_MOVES_ROOK 14
#define MAX_MOVES_QUEEN 27

INT32 KnightMobilityBonus[MAX_MOVES_KNIGHT + 1];
INT32 BishopMobilityBonus[MAX_MOVES_BISHOP + 1];
INT32 RookMobilityBonus[MAX_MOVES_ROOK + 1];
INT32 QueenMobilityBonus[MAX_MOVES_QUEEN + 1];

#define MP 0
#define MK 0
#define MB 0
#define MR 0
#define MQ 0

/* *INDENT-OFF* */
static const int SF_STP_OP[][64] = { 
  {// Pawn
   // A      B      C     D      E      F      G     H
        0,    0,     0,     0,     0,     0,    0,     0,
    MP-28, MP-6, MP+ 4, MP+14, MP+14, MP+ 4, MP-6, MP-28,
    MP-28, MP-6, MP+ 9, MP+36, MP+36, MP+ 9, MP-6, MP-28,
    MP-28, MP-6, MP+17, MP+58, MP+58, MP+17, MP-6, MP-28,
    MP-28, MP-6, MP+17, MP+36, MP+36, MP+17, MP-6, MP-28,
    MP-28, MP-6, MP+ 9, MP+14, MP+14, MP+ 9, MP-6, MP-28,
    MP-28, MP-6, MP+ 4, MP+14, MP+14, MP+ 4, MP-6, MP-28,
        0,    0,     0,     0,     0,     0,    0,     0
  },
  {// Knight
   //  A      B       C      D      E      F      G       H
    MK-135, MK-107, MK-80, MK-67, MK-67, MK-80, MK-107, MK-135,
    MK- 93, MK- 67, MK-39, MK-25, MK-25, MK-39, MK- 67, MK- 93,
    MK- 53, MK- 25, MK+ 1, MK+13, MK+13, MK+ 1, MK- 25, MK- 53,
    MK- 25, MK+  1, MK+27, MK+41, MK+41, MK+27, MK+  1, MK- 25,
    MK- 11, MK+ 13, MK+41, MK+55, MK+55, MK+41, MK+ 13, MK- 11,
    MK- 11, MK+ 13, MK+41, MK+55, MK+55, MK+41, MK+ 13, MK- 11,
    MK- 53, MK- 25, MK+ 1, MK+13, MK+13, MK+ 1, MK- 25, MK- 53,
    MK-193, MK- 67, MK-39, MK-25, MK-25, MK-39, MK- 67, MK-193
  },
  {// Bishop
   // A      B      C      D      E      F      G      H
    MB-40, MB-40, MB-35, MB-30, MB-30, MB-35, MB-40, MB-40,
    MB-17, MB+ 0, MB- 4, MB+ 0, MB+ 0, MB- 4, MB+ 0, MB-17,
    MB-13, MB- 4, MB+ 8, MB+ 4, MB+ 4, MB+ 8, MB- 4, MB-13,
    MB- 8, MB+ 0, MB+ 4, MB+17, MB+17, MB+ 4, MB+ 0, MB- 8,
    MB- 8, MB+ 0, MB+ 4, MB+17, MB+17, MB+ 4, MB+ 0, MB- 8,
    MB-13, MB- 4, MB+ 8, MB+ 4, MB+ 4, MB+ 8, MB- 4, MB-13,
    MB-17, MB+ 0, MB- 4, MB+ 0, MB+ 0, MB- 4, MB+ 0, MB-17,
    MB-17, MB-17, MB-13, MB- 8, MB- 8, MB-13, MB-17, MB-17
  },
  {// Rook
   // A      B     C     D     E     F     G     H
    MR-12, MR-7, MR-2, MR+2, MR+2, MR-2, MR-7, MR-12,
    MR-12, MR-7, MR-2, MR+2, MR+2, MR-2, MR-7, MR-12,
    MR-12, MR-7, MR-2, MR+2, MR+2, MR-2, MR-7, MR-12,
    MR-12, MR-7, MR-2, MR+2, MR+2, MR-2, MR-7, MR-12,
    MR-12, MR-7, MR-2, MR+2, MR+2, MR-2, MR-7, MR-12,
    MR-12, MR-7, MR-2, MR+2, MR+2, MR-2, MR-7, MR-12,
    MR-12, MR-7, MR-2, MR+2, MR+2, MR-2, MR-7, MR-12,
    MR-12, MR-7, MR-2, MR+2, MR+2, MR-2, MR-7, MR-12
  },
  {// Queen
   // A     B     C     D     E     F     G     H
    MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8,
    MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8,
    MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8,
    MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8,
    MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8,
    MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8,
    MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8,
    MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8
  },
  {// King
   //A    B    C    D    E    F    G    H
    287, 311, 262, 214, 214, 262, 311, 287,
    262, 287, 238, 190, 190, 238, 287, 262,
    214, 238, 190, 142, 142, 190, 238, 214,
    190, 214, 167, 119, 119, 167, 214, 190,
    167, 190, 142,  94,  94, 142, 190, 167,
    142, 167, 119,  69,  69, 119, 167, 142,
    119, 142,  94,  46,  46,  94, 142, 119,
     94, 119,  69,  21,  21,  69, 119,  94
  }
};

#define EP 0
#define EK 0
#define EB 0
#define ER 0
#define EQ 0

static const int SF_STP_EG[][64] = {
  {// Pawn
   // A     B     C     D     E     F     G     H
       0,    0,    0,    0,    0,    0,    0,    0,
    EP-8, EP-8, EP-8, EP-8, EP-8, EP-8, EP-8, EP-8,
    EP-8, EP-8, EP-8, EP-8, EP-8, EP-8, EP-8, EP-8,
    EP-8, EP-8, EP-8, EP-8, EP-8, EP-8, EP-8, EP-8,
    EP-8, EP-8, EP-8, EP-8, EP-8, EP-8, EP-8, EP-8,
    EP-8, EP-8, EP-8, EP-8, EP-8, EP-8, EP-8, EP-8,
    EP-8, EP-8, EP-8, EP-8, EP-8, EP-8, EP-8, EP-8,
       0,    0,    0,    0,    0,    0,    0,    0
  },
  {// Knight
   // A       B      C      D      E      F      G      H
    EK-104, EK-79, EK-55, EK-42, EK-42, EK-55, EK-79, EK-104,
    EK- 79, EK-55, EK-30, EK-17, EK-17, EK-30, EK-55, EK- 79,
    EK- 55, EK-30, EK- 6, EK+ 5, EK+ 5, EK- 6, EK-30, EK- 55,
    EK- 42, EK-17, EK+ 5, EK+18, EK+18, EK+ 5, EK-17, EK- 42,
    EK- 42, EK-17, EK+ 5, EK+18, EK+18, EK+ 5, EK-17, EK- 42,
    EK- 55, EK-30, EK- 6, EK+ 5, EK+ 5, EK- 6, EK-30, EK- 55,
    EK- 79, EK-55, EK-30, EK-17, EK-17, EK-30, EK-55, EK- 79,
    EK-104, EK-79, EK-55, EK-42, EK-42, EK-55, EK-79, EK-104
  },
  {// Bishop
   // A      B      C      D      E      F      G      H
    EB-59, EB-42, EB-35, EB-26, EB-26, EB-35, EB-42, EB-59,
    EB-42, EB-26, EB-18, EB-11, EB-11, EB-18, EB-26, EB-42,
    EB-35, EB-18, EB-11, EB- 4, EB- 4, EB-11, EB-18, EB-35,
    EB-26, EB-11, EB- 4, EB+ 4, EB+ 4, EB- 4, EB-11, EB-26,
    EB-26, EB-11, EB- 4, EB+ 4, EB+ 4, EB- 4, EB-11, EB-26,
    EB-35, EB-18, EB-11, EB- 4, EB- 4, EB-11, EB-18, EB-35,
    EB-42, EB-26, EB-18, EB-11, EB-11, EB-18, EB-26, EB-42,
    EB-59, EB-42, EB-35, EB-26, EB-26, EB-35, EB-42, EB-59
  },
  {// Rook
   // A     B     C     D     E     F     G     H
    ER+3, ER+3, ER+3, ER+3, ER+3, ER+3, ER+3, ER+3,
    ER+3, ER+3, ER+3, ER+3, ER+3, ER+3, ER+3, ER+3,
    ER+3, ER+3, ER+3, ER+3, ER+3, ER+3, ER+3, ER+3,
    ER+3, ER+3, ER+3, ER+3, ER+3, ER+3, ER+3, ER+3,
    ER+3, ER+3, ER+3, ER+3, ER+3, ER+3, ER+3, ER+3,
    ER+3, ER+3, ER+3, ER+3, ER+3, ER+3, ER+3, ER+3,
    ER+3, ER+3, ER+3, ER+3, ER+3, ER+3, ER+3, ER+3,
    ER+3, ER+3, ER+3, ER+3, ER+3, ER+3, ER+3, ER+3
  },
  {// Queen
   // A      B      C      D      E      F      G      H
    EQ-80, EQ-54, EQ-42, EQ-30, EQ-30, EQ-42, EQ-54, EQ-80,
    EQ-54, EQ-30, EQ-18, EQ- 6, EQ- 6, EQ-18, EQ-30, EQ-54,
    EQ-42, EQ-18, EQ- 6, EQ+ 6, EQ+ 6, EQ- 6, EQ-18, EQ-42,
    EQ-30, EQ- 6, EQ+ 6, EQ+18, EQ+18, EQ+ 6, EQ- 6, EQ-30,
    EQ-30, EQ- 6, EQ+ 6, EQ+18, EQ+18, EQ+ 6, EQ- 6, EQ-30,
    EQ-42, EQ-18, EQ- 6, EQ+ 6, EQ+ 6, EQ- 6, EQ-18, EQ-42,
    EQ-54, EQ-30, EQ-18, EQ- 6, EQ- 6, EQ-18, EQ-30, EQ-54,
    EQ-80, EQ-54, EQ-42, EQ-30, EQ-30, EQ-42, EQ-54, EQ-80
  },
  {// King
   //A    B    C    D    E    F    G    H
     18,  77, 105, 135, 135, 105,  77,  18,
     77, 135, 165, 193, 193, 165, 135,  77,
    105, 165, 193, 222, 222, 193, 165, 105,
    135, 193, 222, 251, 251, 222, 193, 135,
    135, 193, 222, 251, 251, 222, 193, 135,
    105, 165, 193, 222, 222, 193, 165, 105,
     77, 135, 165, 193, 193, 165, 135,  77,
     18,  77, 105, 135, 135, 105,  77,  18
  }
};
/* *INDENT-ON* */

/* -------------------------------------------------------------------------- */

static const int PAWN_MALUS_DOUBLED_OPENING = 8;
static const int PAWN_MALUS_DOUBLED_ENDGAME = 16;
static const int PAWN_MALUS_ISOLATED_OPENING = 6;
static const int PAWN_MALUS_ISOLATED_ON_OPEN_FILE = 16;
static const int PAWN_MALUS_ISOLATED_ENDGAME = 20;
static const int PAWN_MALUS_ISOLATED_FIXED_OPENING = 4;
static const int PAWN_MALUS_ISOLATED_FIXED_ENDGAME = 6;
static const int PAWN_MALUS_BACKWARD_OPENING = 4;
static const int PAWN_MALUS_BACKWARD_ON_OPEN_FILE = 12;
static const int PAWN_MALUS_BACKWARD_ENDGAME = 10;
static const int PAWN_MALUS_BACKWARD_FIXED_OPENING = 2;
static const int PAWN_MALUS_BACKWARD_FIXED_ENDGAME = 4;

static const int PAWN_CANDIDATE_OPENING_MIN = 5;
static const int PAWN_CANDIDATE_OPENING_MAX = 55;
static const int PAWN_CANDIDATE_ENDGAME_MIN = 10;
static const int PAWN_CANDIDATE_ENDGAME_MAX = 110;

static const int PASSED_PAWN_BONUS_OPENING_MIN = 10;
static const int PASSED_PAWN_BONUS_OPENING_MAX = 70;
static const int PASSED_PAWN_BONUS_ENDGAME_MIN = 20;
static const int PASSED_PAWN_BONUS_ENDGAME_MAX = 140;
static const int PASSED_PAWN_BONUS_UNSTOPPABLE = 800;
static const int PASSED_PAWN_BONUS_NOT_BLOCKED = 60;
static const int PASSED_PAWN_BONUS_ACCOMPANIED = 4;
static const int PASSED_PAWN_MALUS_LACK_OF_SUPPORT = 30;
static const int PASSED_PAWN_ATTACKERDIST_WEIGHT = 15;
static const int PASSED_PAWN_DEFENDERDIST_WEIGHT = 20;
static const int PASSED_PAWN_BONUS_OUTSIDE_OPENING = 8;
static const int PASSED_PAWN_BONUS_OUTSIDE_ENDGAME = 14;
static const int PASSED_PAWN_BONUS_PROTECTED_OPENING = 10;
static const int PASSED_PAWN_BONUS_PROTECTED_ENDGAME = 16;
static const int PASSED_PAWN_BONUS_CONNECTED_OPENING = 16;
static const int PASSED_PAWN_BONUS_CONNECTED_ENDGAME = 24;

static const int KNIGHT_BONUS_ATTACK = 2;
static const int KNIGHT_MALUS_VS_BOTH_WINGS = 10;
static const int KNIGHT_MALUS_VS_OUTSIDE_PASSED = 15;

static const int BISHOP_BONUS_ATTACK = 2;
static const int BISHOP_MALUS_BLOCKED = 50;
static const int BISHOP_MALUS_TRAPPED = 125;
static const int BISHOP_BONUS_PINNING_KNIGHT_OPENING = 15;
static const int BISHOP_BONUS_PINNING_KNIGHT_ENDGAME = 10;

static const int ROOK_BONUS_ON_SEMIOPEN_FILE = 10;
static const int ROOK_BONUS_ON_OPEN_FILE = 20;
static const int ROOK_MALUS_BLOCKED = 48;
static const int ROOK_MALUS_SQUEEZED = 12;
static const int ROOK_BONUS_KING_FILE = 20;
static const int ROOK_BONUS_LATERAL_KING_FILE = 10;
static const int ROOK_BONUS_ON_SEVENTH_RANK_OPENING = 20;
static const int ROOK_BONUS_ON_SEVENTH_RANK_ENDGAME = 40;
static const int ROOK_BONUS_ATTACK = 3;
static const int ROOK_MALUS_BLOCKING_PASSER = 90;

static const int QUEEN_BONUS_ON_SEVENTH_RANK_OPENING = 10;
static const int QUEEN_BONUS_ON_SEVENTH_RANK_ENDGAME = 20;
static const int QUEEN_BONUS_ATTACK = 5;

static const int KINGSAFETY_PAWN_MALUS_DEFENDER[3][8] = {
   {30, 0, 5, 15, 20, 25, 25, 25},
   {55, 0, 15, 40, 50, 55, 55, 55},
   {30, 0, 10, 20, 25, 30, 30, 30}
};

static const int KINGSAFETY_PAWN_BONUS_DEFENDER_DIAG[4][8] = {
   {10, 0, 2, 4, 6, 8, 10, 10},
   {8, 0, 2, 4, 6, 7, 8, 8},
   {6, 0, 2, 3, 4, 5, 6, 6},
   {4, 0, 1, 2, 3, 4, 4, 4}
};

static const int KINGSAFETY_PAWN_BONUS_ATTACKER[3][8] = {
   {5, 0, 35, 15, 5, 0, 0, 0},
   {10, 0, 50, 20, 10, 0, 0, 0},
   {10, 0, 50, 20, 10, 0, 0, 0}
};

#define KING_SAFETY_MALUS_DIM 100

int KING_SAFETY_MALUS[KING_SAFETY_MALUS_DIM];

static const INT32 KING_MALUS_TRAPPED = V(10, 15);

static const int SPACE_BONUS_WEIGHT[16] = {
   0, 2, 6, 11, 17, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24
};

#define KPKP_TABLESIZE (2*32*49*49)

/* -------------------------------------------------------------------------- */

int centerDistance[_64_], centerTaxiDistance[_64_];
int attackPoints[16];
Bitboard weakOutpostSquareCandidates[2];
Bitboard butterflySquares[_64_];
Bitboard lateralSquares[_64_];
Bitboard companionFiles[_64_];
Bitboard passedPawnRectangle[2][_64_];
Bitboard passedPawnCorridor[2][_64_];
int passedPawnDefenderMalus[2][2][_64_][_64_];
Bitboard candidateDefenders[2][_64_];   /* excludes squares of rank */
Bitboard candidateSupporters[2][_64_];  /* includes squares of rank */
Bitboard pawnOpponents[2][_64_];
Bitboard rookTraps[2];
Bitboard rookBlockers[_64_];
Bitboard centralFiles;
Bitboard kingRealm[2][_64_][_64_];
Bitboard attackingRealm[2];
Bitboard homeland[2];
Bitboard troitzkyArea[2];
Bitboard krprkDrawFiles;
Bitboard A1C1, F1H1, A1B1, G1H1;
Bitboard filesBCFG;
KingAttacks kingAttacks[_64_];
int kingChaseMalus[3][_64_];
INT32 sliderAttackPoints[16][16];
INT32 piecePieceAttackBonus[16][16];
MaterialInfo materialInfo[MATERIALINFO_TABLE_SIZE];

/* *INDENT-OFF* */
static const int MALUS_PAWN_SQUARE_TYPE_HR[64] = {
   0, 0, 0, 0, 0, 0, 0, 0,
   1, 1, 2, 2, 2, 2, 1, 1,
   1, 2, 3, 3, 3, 3, 2, 1,
   1, 2, 3, 5, 5, 3, 2, 1,
   1, 2, 3, 5, 5, 3, 2, 1,
   1, 2, 3, 3, 3, 3, 2, 1,
   1, 1, 2, 2, 2, 2, 1, 1,
   0, 0, 0, 0, 0, 0, 0, 0
};

static const int MALUS_KING_SQUARE_HR[64] = {
   15, 15, 15, 15, 15, 15, 15, 15,
   15, 15, 15, 15, 15, 15, 15, 15,
   15, 15, 15, 15, 15, 15, 15, 15,
   15, 15, 15, 15, 15, 15, 15, 15,
   15, 15, 15, 15, 15, 15, 15, 15,
    7, 10, 12, 12, 12, 12, 10,  7,
    2,  2,  4,  8,  8,  4,  2,  2,
    2,  0,  2,  5,  5,  2,  0,  2,
};
/* *INDENT-ON* */

static int MALUS_PAWN_SQUARE_TYPE[64];
static int MALUS_KING_SQUARE[64];

#ifdef GENERATE_TABLES
UINT64 kpkpTempTable[KPKP_TABLESIZE];
#endif

#ifndef NDEBUG
bool debugEval = FALSE;
#endif

int getWhiteKingTableIndex(const Square whiteKingSquare)
{
   const File fileCount = file(whiteKingSquare);
   const Rank rankCount = rank(whiteKingSquare);

   return 4 * rankCount + fileCount;
}

static INLINE unsigned int getKpkpIndexFromSquares(const Square wk,
                                                   const Square bk,
                                                   const Square wp,
                                                   const Square bp)
{
   const unsigned int M1 = 2;
   const unsigned int M2 = 32 * M1;
   const unsigned int M3 = 64 * M2;
   const unsigned int M4 = 49 * M3;
   unsigned int index;

   index = getWhiteKingTableIndex(wk) * M1 + (bk * M2) +
      ((wp - A2) * M3) + ((bp - A2) * M4);

   assert((wp - A2) >= 0);
   assert((bp - A2) >= 0);
   assert((wp - A2) < 49);
   assert((bp - A2) < 49);
   assert(index < (KPKP_TABLESIZE << 6));

   return index;
}

static INLINE unsigned int getKpkpIndex(const Position * position)
{
   Square wk = position->king[WHITE];
   Square bk = position->king[BLACK];
   Square wp = A8, bp = A8;

   if (position->piecesOfType[WHITE_PAWN] != EMPTY_BITBOARD)
   {
      Bitboard whitePawns = position->piecesOfType[WHITE_PAWN];

      wp = getLastSquare(&whitePawns);
   }

   if (position->piecesOfType[BLACK_PAWN] != EMPTY_BITBOARD)
   {
      Bitboard blackPawns = position->piecesOfType[BLACK_PAWN];

      bp = getLastSquare(&blackPawns);
   }

   if (position->activeColor == BLACK)
   {
      const Square tmpWk = wk;
      const Square tmpWp = wp;

#ifndef NDEBUG
      if (debugEval)
      {
         logDebug("before vertical flip\n");
         logDebug("wk=");
         dumpSquare(wk);
         logDebug("bk=");
         dumpSquare(bk);
         logDebug("wp=");
         dumpSquare(wp);
         logDebug("bp=");
         dumpSquare(bp);
         logPosition(position);
      }
#endif

      wk = getFlippedSquare(bk);
      bk = getFlippedSquare(tmpWk);
      wp = (bp == A8 ? bp : getFlippedSquare(bp));
      bp = (tmpWp == A8 ? tmpWp : getFlippedSquare(tmpWp));

#ifndef NDEBUG
      if (debugEval)
      {
         logDebug("after vertical flip\n");
         logDebug("wk=");
         dumpSquare(wk);
         logDebug("bk=");
         dumpSquare(bk);
         logDebug("wp=");
         dumpSquare(wp);
         logDebug("bp=");
         dumpSquare(bp);
         logPosition(position);
      }
#endif
   }

   if (file(wk) >= FILE_E)
   {
#ifndef NDEBUG
      if (debugEval)
      {
         logDebug("before horizontal flip\n");
         logDebug("wk=");
         dumpSquare(wk);
         logDebug("bk=");
         dumpSquare(bk);
         logDebug("wp=");
         dumpSquare(wp);
         logDebug("bp=");
         dumpSquare(bp);
         logPosition(position);
      }
#endif

      wk = getHflippedSquare(wk);
      bk = getHflippedSquare(bk);
      wp = (wp == A8 ? wp : getHflippedSquare(wp));
      bp = (bp == A8 ? bp : getHflippedSquare(bp));

#ifndef NDEBUG
      if (debugEval)
      {
         logDebug("after horizontal flip\n");
         logDebug("wk=");
         dumpSquare(wk);
         logDebug("bk=");
         dumpSquare(bk);
         logDebug("wp=");
         dumpSquare(wp);
         logDebug("bp=");
         dumpSquare(bp);
         logPosition(position);
      }
#endif
   }

   return getKpkpIndexFromSquares(wk, bk, wp, bp);
}

#ifndef NDEBUG
void dumpTableAccess(const Position * position, const int egtbValue,
                     const int internalValue)
{
   int index;

   debugEval = TRUE;
   index = getKpkpIndex(position);

   logDebug("\n### egtbValue=%d internalValue=%d\n\n", egtbValue,
            internalValue);
   dumpPosition(position);
}
#endif

/**
 * Return a tablebase value for a kpkp position.
 *
 * return 0 for draw, 1 for white winning, 2 for black winning
 */
static INLINE int getKpkpValue(const Position * position)
{
   const int WIN_VALUE = 800;
   const unsigned int index = getKpkpIndex(position);
   const unsigned int bucketIndex = index >> 6;
   const unsigned int shiftIndex = index - 64 * bucketIndex;
   const int value = (int) (3 & (kpkpTable[bucketIndex] >> shiftIndex));

   assert((shiftIndex % 2) == 0);
   assert(value >= 0 && value <= 2);

#ifdef INCLUDE_TABLEBASE_ACCESS
#ifndef NDEBUG
   if (tbAvailable != FALSE)
   {
      const int tbValue = probeTablebase(position);

      if (tbValue == 0)
      {
         if (value != 0)
         {
            dumpTableAccess(position, tbValue, value);
         }

         /* assert(value == 0); */
      }

      if (tbValue > 0 && tbValue != TABLEBASE_ERROR)
      {
         if (value != 1)
         {
            dumpTableAccess(position, tbValue, value);
         }

         /* assert(value == 1); */
      }

      if (tbValue < 0)
      {
         if (value != 2)
         {
            dumpTableAccess(position, tbValue, value);
         }

         /* assert(value == 2); */
      }
   }
#endif
#endif

   if (value != 0)
   {
#ifndef NDEBUG
      if (debugEval)
      {
         logDebug("tablevalue=%d\n", value);
         dumpPosition(position);
      }
#endif

      return (value == 1 ? WIN_VALUE : -WIN_VALUE);
   }
   else
   {
      return value;
   }
}

#ifdef GENERATE_TABLES
void cleanPosition(Position * position)
{
   Square square;

   ITERATE(square)
   {
      position->piece[square] = NO_PIECE;
   }

   position->activeColor = WHITE;
   position->enPassantSquare = NO_SQUARE;
}

INLINE static UINT64 getTableValue(int dtmValue)
{
   if (dtmValue == 0)
   {
      return 0;
   }

   return (dtmValue > 0 ? 1 : 2);
}

void updateKpkpValue(const Position * position)
{
   const UINT64 tableValue = getTableValue(probeTablebase(position));
   const unsigned int index = getKpkpIndex(position);
   const unsigned int bucketIndex = index >> 6;
   const unsigned int shiftIndex = index - 64 * bucketIndex;
   const UINT64 mask = ~(((UINT64) 3) << shiftIndex);
   const UINT64 oldBucket = kpkpTempTable[bucketIndex];
   const UINT64 newBucket = (oldBucket & mask) | (tableValue << shiftIndex);

   assert((shiftIndex % 2) == 0);
   assert(tableValue >= 0 && tableValue <= 2);
   assert(bucketIndex < KPKP_TABLESIZE);
   assert(shiftIndex <= 62);

   kpkpTempTable[bucketIndex] = newBucket;
}

void generateKpkpTable()
{
   int i;
   Square wk, bk, wp, bp;
   Position position;
   long count = 0;

   for (i = 0; i < KPKP_TABLESIZE; i++)
   {
      kpkpTempTable[i] = ULONG_ZERO;
   }

   for (wk = A1; wk <= H8; wk++)
   {
      if (file(wk) >= FILE_E)
      {
         continue;              /* assume wk 2b at the queenside */
      }

      for (bk = A1; bk <= H8; bk++)
      {
         if (distance(bk, wk) < 2)
         {
            continue;
         }

         for (wp = A2; wp <= A8; wp++)
         {
            if (wp != A8)
            {
               if (wp == wk || wp == bk)
               {
                  continue;
               }
            }

            for (bp = A2; bp <= A8; bp++)
            {
               if (bp != A8)
               {
                  if (bp == wk || bp == bk || bp == wp)
                  {
                     continue;
                  }
               }

               cleanPosition(&position);
               position.piece[wk] = WHITE_KING;
               position.piece[bk] = BLACK_KING;

               if (wp >= A2 && wp <= H7)
               {
                  position.piece[wp] = WHITE_PAWN;
               }

               if (bp >= A2 && bp <= H7)
               {
                  position.piece[bp] = BLACK_PAWN;
               }

               initializePosition(&position);
               updateKpkpValue(&position);
               count++;

               if ((count & 0xffff) == 0)
               {
                  logDebug("%ld done.\n", count);
               }
            }
         }
      }
   }

   writeTableToFile(&kpkpTempTable[0], KPKP_TABLESIZE, "kpkp.c", "kpkpTable");
}
#endif

INLINE static int quad(int y_min, int y_max, int rank)
{
   const int bonusPerRank[8] = { 0, 0, 0, 26, 77, 154, 256, 0 };

   return y_min + ((y_max - y_min) * bonusPerRank[rank] + 128) / 256;
}

INLINE static int getHomeSecurityWeight(const Position * position)
{
   const int count = getPieceCount(position, WHITE_KNIGHT) +
      getPieceCount(position, WHITE_BISHOP) +
      getPieceCount(position, BLACK_KNIGHT) +
      getPieceCount(position, BLACK_BISHOP);

   return count * count;
}

INLINE static bool squareIsPawnSafe(const EvaluationBase * base,
                                    const Color color, const Square square)
{
   return testSquare(base->pawnAttackableSquares[opponent(color)],
                     square) == FALSE;
}

INLINE static bool hasAttackingBishop(const Position * position,
                                      const Color attackingColor,
                                      const Square square)
{
   const Bitboard attackers =
      ((lightSquares & minValue[square]) != EMPTY_BITBOARD ?
       lightSquares : darkSquares);

   return (bool)
      ((attackers & position->piecesOfType[BISHOP | attackingColor]) !=
       EMPTY_BITBOARD);
}

INLINE static bool pieceIsPinnedByBishop(const Position * position,
                                         const Square pinnedPiece,
                                         const Square pinningPiece,
                                         const Bitboard targets)
{
   return (bool)
      ((getDiaSquaresBehind(position, pinnedPiece, pinningPiece) & targets) !=
       EMPTY_BITBOARD);
}

INLINE static bool pieceIsPinnedByRook(const Position * position,
                                       const Square pinnedPiece,
                                       const Square pinningPiece,
                                       const Bitboard targets)
{
   return (bool)
      ((getOrthoSquaresBehind(position, pinnedPiece, pinningPiece) & targets)
       != EMPTY_BITBOARD);
}

INLINE Piece getPinningOrthoPiece(const Position * position,
                                  const Color pieceColor,
                                  const Square pinnedPieceSquare,
                                  const Square targetSquare,
                                  const bool targetUnprotected,
                                  const bool pinnedPiecePawnProtected)
{
   const Bitboard candidates = (pinnedPiecePawnProtected ?
                                position->piecesOfType[ROOK | pieceColor] :
                                position->piecesOfType[ROOK | pieceColor] |
                                position->piecesOfType[QUEEN | pieceColor]);
   Bitboard pinningCandidate =
      getMagicRookMoves(pinnedPieceSquare, position->allPieces) &
      squaresBehind[pinnedPieceSquare][targetSquare] & candidates;

   if (pinningCandidate != EMPTY_BITBOARD)
   {
      const Square pinningPieceSquare = getLastSquare(&pinningCandidate);
      const Piece pinningPiece = position->piece[pinningPieceSquare];

      if (targetUnprotected)
      {
         return pinningPiece;
      }

      if (pieceType(pinningPiece) == ROOK &&
          pieceType(position->piece[targetSquare]) == QUEEN)
      {
         return pinningPiece;
      }
   }

   return NO_PIECE;
}

INLINE Piece getPinningDiaPiece(const Position * position,
                                const Color pieceColor,
                                const Square pinnedPieceSquare,
                                const Square targetSquare,
                                const bool targetUnprotected,
                                const bool pinnedPiecePawnProtected)
{
   const Bitboard candidates = (pinnedPiecePawnProtected ?
                                position->piecesOfType[BISHOP | pieceColor] :
                                position->piecesOfType[BISHOP | pieceColor] |
                                position->piecesOfType[QUEEN | pieceColor]);
   Bitboard pinningCandidate =
      getMagicBishopMoves(pinnedPieceSquare, position->allPieces) &
      squaresBehind[pinnedPieceSquare][targetSquare] & candidates;

   if (pinningCandidate != EMPTY_BITBOARD)
   {
      const Square pinningPieceSquare = getLastSquare(&pinningCandidate);
      const Piece pinningPiece = position->piece[pinningPieceSquare];

      if (targetUnprotected)
      {
         return pinningPiece;
      }

      if (pieceType(pinningPiece) == BISHOP &&
          pieceType(position->piece[targetSquare]) == QUEEN)
      {
         const PieceType targetPieceType =
            pieceType(position->piece[targetSquare]);

         if (targetPieceType == QUEEN || targetPieceType == ROOK)
         {
            return pinningPiece;
         }
      }
   }

   return NO_PIECE;
}

INLINE static void getPawnInfo(const Position * position,
                               EvaluationBase * base)
{
   const Bitboard white = position->piecesOfType[WHITE_PAWN];
   const Bitboard black = position->piecesOfType[BLACK_PAWN];
   Bitboard whiteLateralSquares, blackLateralSquares;
   Bitboard whiteSwamp, blackSwamp;
   Bitboard pawnAttackableSquaresWhite, pawnAttackableSquaresBlack;
   register Bitboard tmp1, tmp2;
   Bitboard blocked, free;
   Square square;

   /* Calculate upward and downward realms */
   tmp1 = (white << 8) | (white << 16) | (white << 24);
   tmp1 |= (tmp1 << 24);
   tmp2 = (white >> 8) | (white >> 16) | (white >> 24);
   tmp2 |= (tmp2 >> 24);

   base->doubledPawns[WHITE] = white & tmp2;
   base->upwardRealm[WHITE] = (tmp1 = tmp1 | white);
   pawnAttackableSquaresWhite = ((tmp1 & nonA) << 7) | ((tmp1 & nonH) << 9);
   base->downwardRealm[WHITE] = tmp2;

   /* Calculate upward and downward realms */
   tmp1 = (black >> 8) | (black >> 16) | (black >> 24);
   tmp1 |= (tmp1 >> 24);
   tmp2 = (black << 8) | (black << 16) | (black << 24);
   tmp2 |= (tmp2 << 24);

   base->doubledPawns[BLACK] = black & tmp2;
   base->upwardRealm[BLACK] = (tmp1 = tmp1 | black);
   pawnAttackableSquaresBlack = ((tmp1 & nonA) >> 9) | ((tmp1 & nonH) >> 7);
   base->downwardRealm[BLACK] = tmp2;

   /* Calculate the squares protected by a pawn */
   whiteLateralSquares = ((white & nonA) >> 1) | ((white & nonH) << 1);
   base->pawnProtectedSquares[WHITE] = whiteLateralSquares << 8;
   blackLateralSquares = ((black & nonA) >> 1) | ((black & nonH) << 1);
   base->pawnProtectedSquares[BLACK] = blackLateralSquares >> 8;

   /* Identify the passed pawns */
   whiteSwamp = base->downwardRealm[BLACK] | base->upwardRealm[WHITE] |
      pawnAttackableSquaresWhite;
   blackSwamp = base->downwardRealm[WHITE] | base->upwardRealm[BLACK] |
      pawnAttackableSquaresBlack;

   base->passedPawns[WHITE] = white & ~blackSwamp;
   base->passedPawns[BLACK] = black & ~whiteSwamp;

   /* Calculate the weak pawns */
   tmp2 = ~(white | black | base->pawnProtectedSquares[BLACK]);
   tmp1 = (whiteLateralSquares & tmp2) >> 8;
   tmp1 |= (tmp1 & squaresOfRank[RANK_3] & tmp2) >> 8;
   base->weakPawns[WHITE] =
      (white & ~(pawnAttackableSquaresWhite | whiteLateralSquares | tmp1));

   tmp2 = ~(white | black | base->pawnProtectedSquares[WHITE]);
   tmp1 = (blackLateralSquares & tmp2) << 8;
   tmp1 |= (tmp1 & squaresOfRank[RANK_6] & tmp2) << 8;
   base->weakPawns[BLACK] =
      (black & ~(pawnAttackableSquaresBlack | blackLateralSquares | tmp1));

   base->weakOutpostSquares[WHITE] = base->upwardRealm[WHITE] &
      ~(pawnAttackableSquaresWhite |
        base->downwardRealm[BLACK] | white | black) &
      weakOutpostSquareCandidates[WHITE];

   base->weakOutpostSquares[BLACK] = base->upwardRealm[BLACK] &
      ~(pawnAttackableSquaresBlack |
        base->downwardRealm[WHITE] | white | black) &
      weakOutpostSquareCandidates[BLACK];

   /* Calculate the candidates */
   base->candidatePawns[WHITE] = white & ~base->passedPawns[WHITE] &
      (pawnAttackableSquaresWhite | whiteLateralSquares) &
      ~(base->upwardRealm[BLACK] | base->downwardRealm[WHITE]);

   base->candidatePawns[BLACK] = black & ~base->passedPawns[BLACK] &
      (pawnAttackableSquaresBlack | blackLateralSquares) &
      ~(base->upwardRealm[WHITE] | base->downwardRealm[BLACK]);

#ifdef BONUS_HIDDEN_PASSER
   /* Calculate the hidden candidates */
   base->hiddenCandidatePawns[WHITE] = white & (black >> 8) &
      ~pawnAttackableSquaresBlack & ~(blackLateralSquares) &
      (squaresOfRank[RANK_5] | squaresOfRank[RANK_6]) &
      base->pawnProtectedSquares[WHITE];

   base->hiddenCandidatePawns[BLACK] = black & (white << 8) &
      ~pawnAttackableSquaresWhite & ~(whiteLateralSquares) &
      (squaresOfRank[RANK_4] | squaresOfRank[RANK_3]) &
      base->pawnProtectedSquares[BLACK];
#endif

   tmp1 = black & base->pawnProtectedSquares[BLACK];
   tmp2 = ((tmp1 & nonA) >> 9) & ((tmp1 & nonH) >> 7);
   tmp2 &= ~pawnAttackableSquaresWhite;
   tmp1 = tmp2 | (tmp2 >> 8);
   base->fixedPawns[WHITE] = tmp1 | (tmp1 >> 16) | (tmp1 >> 32);

   tmp1 = white & base->pawnProtectedSquares[WHITE];
   tmp2 = ((tmp1 & nonA) << 7) & ((tmp1 & nonH) << 9);
   tmp2 &= ~pawnAttackableSquaresBlack;
   tmp1 = tmp2 | (tmp2 << 8);
   base->fixedPawns[BLACK] = tmp1 | (tmp1 << 16) | (tmp1 << 32);

#ifdef BONUS_HIDDEN_PASSER
   base->hasPassersOrCandidates[WHITE] = (bool)
      (base->passedPawns[WHITE] != EMPTY_BITBOARD ||
       base->candidatePawns[WHITE] != EMPTY_BITBOARD ||
       base->hiddenCandidatePawns[WHITE] != EMPTY_BITBOARD);

   base->hasPassersOrCandidates[BLACK] = (bool)
      (base->passedPawns[BLACK] != EMPTY_BITBOARD ||
       base->candidatePawns[BLACK] != EMPTY_BITBOARD ||
       base->hiddenCandidatePawns[BLACK] != EMPTY_BITBOARD);
#endif

   tmp1 = (white << 8) & ~(white | black);
   tmp2 = white | (tmp1 & ~base->pawnProtectedSquares[BLACK]);
   tmp2 |= tmp1 & base->pawnProtectedSquares[WHITE];
   tmp1 &= squaresOfRank[RANK_3] & ~base->pawnProtectedSquares[BLACK];
   tmp1 = (tmp1 << 8) & ~(white | black);
   tmp2 |= tmp1 & ~base->pawnProtectedSquares[BLACK];
   tmp2 |= tmp1 & base->pawnProtectedSquares[WHITE];
   base->pawnAttackableSquares[WHITE] =
      ((tmp2 & nonA) << 7) | ((tmp2 & nonH) << 9);

   /*dumpBitboard(base->pawnAttackableSquares[WHITE], "pawnAttackable white");
      dumpPosition(position); */

   tmp1 = (black >> 8) & ~(white | black);
   tmp2 = black | (tmp1 & ~base->pawnProtectedSquares[WHITE]);
   tmp2 |= tmp1 & base->pawnProtectedSquares[BLACK];
   tmp1 &= squaresOfRank[RANK_6] & ~base->pawnProtectedSquares[WHITE];
   tmp1 = (tmp1 >> 8) & ~(white | black);
   tmp2 |= tmp1 & ~base->pawnProtectedSquares[WHITE];
   tmp2 |= tmp1 & base->pawnProtectedSquares[BLACK];
   base->pawnAttackableSquares[BLACK] =
      ((tmp2 & nonA) >> 9) | ((tmp2 & nonH) >> 7);

   base->pawnLightSquareMalus[WHITE] = base->pawnLightSquareMalus[BLACK] = 0;
   base->pawnDarkSquareMalus[WHITE] = base->pawnDarkSquareMalus[BLACK] = 0;
   blocked = white & (black >> 8);
   free = white & ~blocked;

   ITERATE_BITBOARD(&blocked, square)
   {
      const int malus =
         MALUS_PAWN_SQUARE_TYPE[square] + MALUS_PAWN_SQUARE_TYPE[square];

      if (testSquare(lightSquares, square))
      {
         base->pawnLightSquareMalus[WHITE] += malus;
      }
      else
      {
         base->pawnDarkSquareMalus[WHITE] += malus;
      }
   }

   ITERATE_BITBOARD(&free, square)
   {
      const int malus = MALUS_PAWN_SQUARE_TYPE[square];

      if (testSquare(lightSquares, square))
      {
         base->pawnLightSquareMalus[WHITE] += malus;
      }
      else
      {
         base->pawnDarkSquareMalus[WHITE] += malus;
      }
   }

   blocked = black & (white << 8);
   free = black & ~blocked;

   ITERATE_BITBOARD(&blocked, square)
   {
      const int malus =
         MALUS_PAWN_SQUARE_TYPE[square] + MALUS_PAWN_SQUARE_TYPE[square];

      if (testSquare(lightSquares, square))
      {
         base->pawnLightSquareMalus[BLACK] += malus;
      }
      else
      {
         base->pawnDarkSquareMalus[BLACK] += malus;
      }
   }

   ITERATE_BITBOARD(&free, square)
   {
      const int malus = MALUS_PAWN_SQUARE_TYPE[square];

      if (testSquare(lightSquares, square))
      {
         base->pawnLightSquareMalus[BLACK] += malus;
      }
      else
      {
         base->pawnDarkSquareMalus[BLACK] += malus;
      }
   }

   base->chainPawns[WHITE] = white &
      (base->pawnProtectedSquares[WHITE] | whiteLateralSquares);
   base->chainPawns[BLACK] = black &
      (base->pawnProtectedSquares[BLACK] | blackLateralSquares);
}

bool pawnIsPassed(const Position * position, const Square pawnSquare,
                  const Color pawnColor)
{
   const Color defenderColor = opponent(pawnColor);
   const Bitboard blockers = position->piecesOfType[PAWN | defenderColor] &
      (candidateDefenders[pawnColor][pawnSquare] |
       passedPawnCorridor[pawnColor][pawnSquare]);

   return (bool) (blockers == EMPTY_BITBOARD);
}

static int getPassedPawnDefenderMalus(const Color pawnColor,
                                      const Color activeColor,
                                      const Square passedPawnSquare,
                                      const Square defenderSquare)
{
   const int pawnDirection = (pawnColor == WHITE ? 8 : -8);
   const Square stopSquare = (Square) (passedPawnSquare + pawnDirection);
   const Square rectangleSquare =
      (pawnColor == activeColor ?
       passedPawnSquare : (Square) (passedPawnSquare - pawnDirection));
   const bool kingInRectangle =
      testSquare(passedPawnRectangle[pawnColor][rectangleSquare],
                 defenderSquare);
   const int defenderDistWeight =
      (kingInRectangle ? (2 * PASSED_PAWN_DEFENDERDIST_WEIGHT) / 3 :
       PASSED_PAWN_DEFENDERDIST_WEIGHT);

   return distance(stopSquare, defenderSquare) * defenderDistWeight;
}

INLINE bool passerWalks(const Position * position, const Square passerSquare,
                        const Color passerColor)
{
   const Square attackerKingSquare = position->king[passerColor];
   const Square defenderKingSquare = position->king[opponent(passerColor)];
   const int attackerDistance = distance(attackerKingSquare, passerSquare);
   const Rank kingRank = colorRank(passerColor, attackerKingSquare);
   const File passerFile = file(passerSquare);

   if (passerFile >= FILE_B && passerFile <= FILE_G)
   {
      if ((kingRank == RANK_6 || kingRank == RANK_7) &&
          kingRank > colorRank(passerColor, passerSquare) &&
          abs(file(attackerKingSquare) - passerFile) <= 1 &&
          attackerDistance <= 2)
      {
         if (position->activeColor == passerColor ||
             attackerDistance == 1 ||
             distance(defenderKingSquare, passerSquare) > 1)
         {
            return TRUE;
         }
      }

      /*
         if (kingRank == colorRank(passerColor, passerSquare) + 2 &&
         abs(file(attackerKingSquare) - passerFile) <= 1 &&
         attackerDistance <= 2)
         {
         if (position->activeColor == passerColor ||
         attackerDistance == 1 ||
         distance(defenderKingSquare, passerSquare) > 1)
         {
         return TRUE;
         }
         }
       */
   }
   else if ((kingRank == RANK_7 || kingRank == RANK_8) &&
            abs(file(attackerKingSquare) - passerFile) == 1 &&
            attackerDistance <= 2)
   {
      if (position->activeColor == passerColor ||
          attackerDistance == 1 ||
          distance(defenderKingSquare, passerSquare) > 1)
      {
         return TRUE;
      }
   }

   return FALSE;
}

INLINE bool isBlockingSquare(const Position * position,
                             const EvaluationBase * base, const Square square,
                             const Color blockingColor)
{
   const Square blockedSquare = (Square)
      (blockingColor == WHITE ? square + 8 : square - 8);
   const Piece blockedPawn = (Piece) (PAWN | opponent(blockingColor));

   return (bool) (position->piece[blockedSquare] == blockedPawn &&
                  squareIsPawnSafe(base, blockingColor, square));
}

INLINE int pawnDefenderDistance(const Square defenderSquare,
                                const Color defenderColor,
                                const Square pawnSquare)
{
   const int defenderRank = rank(defenderSquare);
   const int pawnRank = rank(pawnSquare);

   if (defenderColor == WHITE)
   {
      return (defenderRank <= pawnRank ?
              abs(file(defenderSquare) - file(pawnSquare)) :
              distance(defenderSquare, pawnSquare));
   }
   else
   {
      return (defenderRank >= pawnRank ?
              abs(file(defenderSquare) - file(pawnSquare)) :
              distance(defenderSquare, pawnSquare));
   }
}

INLINE static int getMobilityCount(const EvaluationBase * base,
                                   const Bitboard moves, const Color color)
{
   return getNumberOfSetSquares(moves & base->countedSquares[color]);
}

INLINE static Piece getOrthoBatteryPiece(const Position * position,
                                         const Bitboard moves,
                                         const Square attackerSquare,
                                         const Color kingColor)
{
   const Square kingSquare = position->king[kingColor];
   Bitboard middlePiece = EMPTY_BITBOARD;

   if (testSquare(generalMoves[ROOK][kingSquare], attackerSquare))
   {
      middlePiece = generalMoves[ROOK][attackerSquare] &
         moves & getMagicRookMoves(kingSquare, position->allPieces);

      if (middlePiece != EMPTY_BITBOARD)
      {
         const Square pieceSquare = getLastSquare(&middlePiece);

         return position->piece[pieceSquare];
      }
   }

   return NO_PIECE;
}

INLINE static Piece getDiaBatteryPiece(const Position * position,
                                       const Bitboard moves,
                                       const Square attackerSquare,
                                       const Color kingColor)
{
   const Square kingSquare = position->king[kingColor];
   Bitboard middlePiece = EMPTY_BITBOARD;

   if (testSquare(generalMoves[BISHOP][kingSquare], attackerSquare))
   {
      middlePiece = generalMoves[BISHOP][attackerSquare] &
         moves & getMagicBishopMoves(kingSquare, position->allPieces);

      if (middlePiece != EMPTY_BITBOARD)
      {
         const Square pieceSquare = getLastSquare(&middlePiece);

         return position->piece[pieceSquare];
      }
   }

   return NO_PIECE;
}

INLINE static bool grantSeventhRankBonus(const Position * position,
                                         const Color color,
                                         const Color oppColor,
                                         const Square pieceSquare)
{
   if (colorRank(color, pieceSquare) == RANK_7)
   {
      const static Rank seventhRankByColor[2] = { RANK_7, RANK_2 };
      const Bitboard seventhRank = squaresOfRank[seventhRankByColor[color]];

      if ((seventhRank & position->piecesOfType[PAWN | oppColor]) ||
          (colorRank(color, position->king[oppColor]) >= RANK_7))
      {
         return TRUE;
      }
   }

   return FALSE;
}

#define PERSPECTIVE_WHITE
#include "evaluationc.c"
#undef PERSPECTIVE_WHITE
#include "evaluationc.c"

INLINE static void evaluatePawns(const Position * position,
                                 EvaluationBase * base)
{
#ifdef REDUCED_PAWN_EVAL
   const static INT32 isolatedMalusPerFile[8] = {
      V(9, 9), V(13, 11), V(14, 11), V(14, 11),
      V(14, 11), V(14, 11), V(13, 11), V(9, 9)
   };
   const static INT32 isolatedMalusPerOpenFile[8] = {
      V(13, 14), V(19, 16), V(21, 16), V(21, 16),
      V(21, 16), V(21, 16), V(19, 16), V(13, 14)
   };
   const static INT32 backwardMalusPerFile[8] = {
      V(7, 9), V(10, 10), V(12, 10), V(12, 10),
      V(12, 10), V(12, 10), V(10, 10), V(7, 9)
   };
   const static INT32 backwardMalusPerOpenFile[8] = {
      V(11, 13), V(15, 14), V(18, 14), V(18, 14),
      V(18, 14), V(18, 14), V(15, 14), V(11, 13)
   };
   const static INT32 candidateBonusPerRank[8] = {
      V(0, 0), V(2, 5), V(2, 5), V(5, 11),
      V(13, 26), V(32, 64), V(0, 0), V(0, 0)
   };
   /*
      const static INT32 candidateBonusPerRank[8] = {
      V(0, 0), V(2, 4), V(2, 4), V(5, 9),
      V(12, 21), V(30, 51), V(0, 0), V(0, 0)
      };
    */
#else
   const static INT32 isolatedMalusPerFile[8] = {
      V(10, 12), V(14, 14), V(16, 14), V(16, 14),
      V(16, 14), V(16, 14), V(14, 14), V(10, 12)
   };
   const static INT32 isolatedMalusPerOpenFile[8] = {
      V(15, 18), V(21, 21), V(24, 21), V(24, 21),
      V(24, 21), V(24, 21), V(21, 21), V(15, 18)
   };
   const static INT32 backwardMalusPerFile[8] = {
      V(8, 11), V(11, 12), V(13, 12), V(13, 12),
      V(13, 12), V(13, 12), V(11, 12), V(8, 11)
   };
   const static INT32 backwardMalusPerOpenFile[8] = {
      V(12, 16), V(16, 18), V(19, 18), V(19, 18),
      V(19, 18), V(19, 18), V(16, 18), V(12, 16)
   };
   const static INT32 candidateBonusPerRank[8] = {
      V(0, 0), V(2, 5), V(2, 5), V(5, 11),
      V(13, 26), V(32, 64), V(0, 0), V(0, 0)
   };
#endif

   Square square;
   Bitboard pieces = (base->weakPawns[WHITE] | base->weakPawns[BLACK]);

   ITERATE_BITBOARD(&pieces, square)
   {
      const Color pawnColor = pieceColor(position->piece[square]);
      const Color oppColor = opponent(pawnColor);
      const File pawnfile = file(square);
      const bool isolated = (bool)
         ((companionFiles[square] &
           position->piecesOfType[position->piece[square]]) ==
          EMPTY_BITBOARD);
      const bool onOpenFile = (bool)
         (testSquare(base->upwardRealm[oppColor], square) == FALSE &&
          testSquare(base->doubledPawns[pawnColor], square) == FALSE);

      if (isolated)
      {
         if (onOpenFile)
         {
            addEvalMalusForColor(base, pawnColor,
                                 isolatedMalusPerOpenFile[pawnfile]);
         }
         else
         {
            addEvalMalusForColor(base, pawnColor,
                                 isolatedMalusPerFile[pawnfile]);
         }

         if (testSquare(base->fixedPawns[pawnColor], square))
         {
            addEvalMalusForColor(base, pawnColor, V(2, 3));
         }
      }
      else                      /* backward */
      {
         if (onOpenFile)
         {
            addEvalMalusForColor(base, pawnColor,
                                 backwardMalusPerOpenFile[pawnfile]);
         }
         else
         {
            addEvalMalusForColor(base, pawnColor,
                                 backwardMalusPerFile[pawnfile]);
         }

         if (testSquare(base->fixedPawns[pawnColor], square))
         {
            addEvalMalusForColor(base, pawnColor, V(2, 3));
         }
      }
   }

   pieces = (base->candidatePawns[WHITE] | base->candidatePawns[BLACK]);

   ITERATE_BITBOARD(&pieces, square)
   {
      const Color pawnColor = pieceColor(position->piece[square]);
      const Bitboard supporters = candidateSupporters[pawnColor][square] &
         position->piecesOfType[PAWN | pawnColor];
      const Bitboard defenders = candidateDefenders[pawnColor][square] &
         position->piecesOfType[PAWN | opponent(pawnColor)];

      if (getNumberOfSetSquares(supporters) >=
          getNumberOfSetSquares(defenders))
      {
         const Bitboard ownDefenders =
            generalMoves[PAWN | opponent(pawnColor)][square] &
            position->piecesOfType[PAWN | pawnColor];
         const Bitboard attackers =
            generalMoves[PAWN | pawnColor][square] &
            position->piecesOfType[PAWN | opponent(pawnColor)];

         if (getNumberOfSetSquares(ownDefenders) >=
             getNumberOfSetSquares(attackers))
         {
            const Rank pawnRank = colorRank(pawnColor, square);

            addEvalBonusForColor(base, pawnColor,
                                 candidateBonusPerRank[pawnRank]);
         }
      }
   }

   evaluateWhitePawns(base);
   evaluateBlackPawns(base);
}

INLINE static void evaluatePassedPawns(const Position * position,
                                       EvaluationBase * base)
{
   Square square;
   Bitboard pieces = base->passedPawns[WHITE];

   ITERATE_BITBOARD(&pieces, square)
   {
      evaluateWhitePasser(position, base, square);
   }

   pieces = base->passedPawns[BLACK];

   ITERATE_BITBOARD(&pieces, square)
   {
      evaluateBlackPasser(position, base, square);
   }
}

INLINE static void evaluateWhiteTrappedBishops(const Position * position,
                                               EvaluationBase * base)
{
   if ((position->piece[A7] == WHITE_BISHOP &&
        position->piece[B6] == BLACK_PAWN) ||
       (position->piece[B8] == WHITE_BISHOP &&
        position->piece[C7] == BLACK_PAWN) ||
       (position->piece[H7] == WHITE_BISHOP &&
        position->piece[G6] == BLACK_PAWN) ||
       (position->piece[G8] == WHITE_BISHOP &&
        position->piece[F7] == BLACK_PAWN))
   {
      base->balance -= V(BISHOP_MALUS_TRAPPED, BISHOP_MALUS_TRAPPED);
   }

   if ((position->piece[A6] == WHITE_BISHOP &&
        position->piece[B5] == BLACK_PAWN) ||
       (position->piece[H6] == WHITE_BISHOP &&
        position->piece[G5] == BLACK_PAWN))
   {
      base->balance -= V(BISHOP_MALUS_TRAPPED / 2, BISHOP_MALUS_TRAPPED / 2);
   }

   if ((position->piece[C1] == WHITE_BISHOP &&
        position->piece[D2] == WHITE_PAWN &&
        position->piece[D3] != NO_PIECE) ||
       (position->piece[F1] == WHITE_BISHOP &&
        position->piece[E2] == WHITE_PAWN && position->piece[E3] != NO_PIECE))
   {
      base->balance -= V(BISHOP_MALUS_BLOCKED, BISHOP_MALUS_BLOCKED);
   }
}

INLINE static void evaluateBlackTrappedBishops(const Position * position,
                                               EvaluationBase * base)
{
   if ((position->piece[A2] == BLACK_BISHOP &&
        position->piece[B3] == WHITE_PAWN) ||
       (position->piece[B1] == BLACK_BISHOP &&
        position->piece[C2] == WHITE_PAWN) ||
       (position->piece[H2] == BLACK_BISHOP &&
        position->piece[G3] == WHITE_PAWN) ||
       (position->piece[G1] == BLACK_BISHOP &&
        position->piece[F2] == WHITE_PAWN))
   {
      base->balance += V(BISHOP_MALUS_TRAPPED, BISHOP_MALUS_TRAPPED);
   }

   if ((position->piece[A3] == BLACK_BISHOP &&
        position->piece[B4] == WHITE_PAWN) ||
       (position->piece[H3] == BLACK_BISHOP &&
        position->piece[G4] == WHITE_PAWN))
   {
      base->balance += V(BISHOP_MALUS_TRAPPED / 2, BISHOP_MALUS_TRAPPED / 2);
   }

   if ((position->piece[C8] == BLACK_BISHOP &&
        position->piece[D7] == BLACK_PAWN &&
        position->piece[D6] != NO_PIECE) ||
       (position->piece[F8] == BLACK_BISHOP &&
        position->piece[E7] == BLACK_PAWN && position->piece[E6] != NO_PIECE))
   {
      base->balance += V(BISHOP_MALUS_BLOCKED, BISHOP_MALUS_BLOCKED);
   }
}

INLINE static int getSafetyMalusOfKingFile(const Position * position,
                                           const int file,
                                           const Square kingSquare,
                                           const int fileType,
                                           const Color color)
{
   return (color == WHITE ?
           getPawnSafetyMalusOfWhiteKingFile(position, file, kingSquare,
                                             fileType) :
           getPawnSafetyMalusOfBlackKingFile(position, file, kingSquare,
                                             fileType));
}

INLINE static int getSafetyMalusOfKingSquare(const Position * position,
                                             const Square kingSquare,
                                             const Color color)
{
   const int kingFile = file(kingSquare);
   int malus =
      getSafetyMalusOfKingFile(position, kingFile, kingSquare, 1, color);

   if (kingFile > FILE_A)
   {
      const int fileType = (kingFile <= FILE_D ? 0 : 2);

      malus += getSafetyMalusOfKingFile(position, kingFile - 1, kingSquare,
                                        fileType, color);
   }

   if (kingFile < FILE_H)
   {
      const int fileType = (kingFile <= FILE_D ? 2 : 0);

      malus += getSafetyMalusOfKingFile(position, kingFile + 1, kingSquare,
                                        fileType, color);
   }

   if (malus == 0)
   {
      malus = 10;               /* malus for weak back rank */
   }

   return malus;
}

static int getPawnShelterMalus(const Position * position, const Color color,
                               KingSafetyHashInfo * kingSafetyHashtable)
{
   const Bitboard hashValue = getKingPawnSafetyHashValue(position, color);
   KingSafetyHashInfo *kingSafetyHashInfo =
      &kingSafetyHashtable[hashValue & KINGSAFETY_HASHTABLE_MASK];

   if (kingSafetyHashInfo->hashValue == hashValue &&
       kingSafetyHashInfo->hashValue != 0)
   {
      return kingSafetyHashInfo->safetyMalus;
   }
   else
   {
      const static int rankByColor[2] = { RANK_1, RANK_8 };
      int cr00, cr000;
      const Square kingSquare = position->king[color];
      int pawnShelterMalus = 0, castlingShelterMalus = 0;

      if (color == WHITE)
      {
         cr00 = WHITE_00, cr000 = WHITE_000;
      }
      else
      {
         cr00 = BLACK_00, cr000 = BLACK_000;
      }

      pawnShelterMalus = castlingShelterMalus =
         getSafetyMalusOfKingSquare(position, kingSquare, color);

      if (position->castlingRights & cr00)
      {
         const Square kingSquare = getSquare(FILE_G, rankByColor[color]);
         const int malus00 =
            getSafetyMalusOfKingSquare(position, kingSquare, color);

         castlingShelterMalus = min(malus00, castlingShelterMalus);
      }

      if (position->castlingRights & cr000)
      {
         const Square kingSquare = getSquare(FILE_B, rankByColor[color]);
         const int malus000 =
            getSafetyMalusOfKingSquare(position, kingSquare, color);

         castlingShelterMalus = min(malus000, castlingShelterMalus);
      }

      pawnShelterMalus = (pawnShelterMalus + castlingShelterMalus) / 2;
      kingSafetyHashInfo->hashValue = hashValue;
      kingSafetyHashInfo->safetyMalus = pawnShelterMalus;

      return pawnShelterMalus;
   }
}

static int getKingSafetyMalus(const Position * position,
                              EvaluationBase * base, const Color color)
{
   const int pawnShelterMalus =
      getPawnShelterMalus(position, color, base->kingsafetyHashtable);
   const Color oppColor = opponent(color);
   const Square kingSquare = position->king[color];
   const Bitboard corona = getKingMoves(kingSquare);
   const Bitboard protectingPawn = corona &
      passedPawnCorridor[color][kingSquare];
   const Bitboard pawnAttackers =
      position->piecesOfType[PAWN | oppColor] & kingAttacks[kingSquare].
      pawnAttackers[oppColor] & ~protectingPawn;
   const int numAttackers = getEndgameValue(base->attackInfo[oppColor]) +
      getNumberOfSetSquares(pawnAttackers) / 2;
   int attackUnits = 0;

   if (base->evaluateKingSafety[color] && numAttackers >= 2 &&
       base->kingSquaresAttackCount[oppColor] > 0)
   {
      const int kingSquareWeight = 12 + getPieceWeight(position, oppColor);
      const Square relativeKingSquare =
         (color == WHITE ? kingSquare : getFlippedSquare(kingSquare));
      const int kingSquareMalus =
         (MALUS_KING_SQUARE[relativeKingSquare] * kingSquareWeight) / 24;
      const int attackersWeight = getOpeningValue(base->attackInfo[oppColor]);
      const Bitboard oppQueenAttacks = base->queenAttackedSquares[oppColor];
      const Bitboard oppAttacks = base->attackedSquares[oppColor] |
         getKingMoves(position->king[oppColor]);
      const Bitboard attackedSquares =
         corona & (oppAttacks | oppQueenAttacks);
      const Bitboard defendedSquares =
         base->attackedSquares[color] | base->queenAttackedSquares[color];
      const Bitboard undefendedSquares = attackedSquares & ~defendedSquares;
      const Bitboard safeQueenChecks =
         oppQueenAttacks & oppAttacks & undefendedSquares;
      const int squareAttackCount =
         (base->kingSquaresAttackCount[oppColor] +
          getNumberOfSetSquares(undefendedSquares));
      const Bitboard safeChecks =
         ~(position->piecesOfColor[oppColor] | defendedSquares);
      const Bitboard safeOrthoChecks = safeChecks &
         getMagicRookMoves(kingSquare, position->allPieces);
      const Bitboard safeDiaChecks = safeChecks &
         getMagicBishopMoves(kingSquare, position->allPieces);
      const Bitboard safeKnightChecks = safeChecks &
         getKnightMoves(kingSquare);
      const Bitboard batteryAttackers = safeChecks &
         base->batteryAttackers[oppColor];

#ifdef MALUS_QUEEN_DEFENDER_DISTANCE
      Piece queens = (Piece) (QUEEN | color);
      const int queenDistance =
         getMinimalTaxiDistance(position, kingSquare, queens);
#endif

      attackUnits =
         min(25, numAttackers * attackersWeight / 2) +
         3 * squareAttackCount + kingSquareMalus + pawnShelterMalus / 16;

      if (batteryAttackers != EMPTY_BITBOARD)
      {
         const int numAttackers = getNumberOfSetSquares(batteryAttackers);
         const int activeColorFactor =
            (oppColor == position->activeColor ? 2 : 1);

         attackUnits += numAttackers * activeColorFactor;
      }

      if (safeQueenChecks != EMPTY_BITBOARD)
      {
         const int numChecks = getNumberOfSetSquares(safeQueenChecks);
         const int activeColorFactor =
            (oppColor == position->activeColor ? 6 : 3);

         attackUnits += numChecks * activeColorFactor;
      }

      if (safeOrthoChecks != EMPTY_BITBOARD)
      {
         const Bitboard queenChecks =
            safeOrthoChecks & base->queenAttackedSquares[oppColor];
         const Bitboard rookChecks =
            safeOrthoChecks & base->rookAttackedSquares[oppColor];

         if (queenChecks != EMPTY_BITBOARD)
         {
            attackUnits += getNumberOfSetSquares(queenChecks);
         }

         if (rookChecks != EMPTY_BITBOARD)
         {
            attackUnits += getNumberOfSetSquares(rookChecks);
         }
      }

      if (safeDiaChecks != EMPTY_BITBOARD)
      {
         const Bitboard queenChecks =
            safeDiaChecks & base->queenAttackedSquares[oppColor];
         const Bitboard bishopChecks =
            safeDiaChecks & base->bishopAttackedSquares[oppColor];

         if (queenChecks != EMPTY_BITBOARD)
         {
            attackUnits += getNumberOfSetSquares(queenChecks);
         }

         if (bishopChecks != EMPTY_BITBOARD)
         {
            attackUnits += getNumberOfSetSquares(bishopChecks);
         }
      }

      if (safeKnightChecks != EMPTY_BITBOARD)
      {
         const Bitboard knightChecks =
            safeKnightChecks & base->knightAttackedSquares[oppColor];

         if (knightChecks != EMPTY_BITBOARD)
         {
            attackUnits += getNumberOfSetSquares(knightChecks);
         }
      }

#ifdef MALUS_QUEEN_DEFENDER_DISTANCE
      attackUnits += queenDistance / 2;
#endif

      if (attackUnits >= KING_SAFETY_MALUS_DIM)
      {
         attackUnits = KING_SAFETY_MALUS_DIM - 1;
      }
   }

   return KING_SAFETY_MALUS[attackUnits] + pawnShelterMalus;
}

INLINE static bool kingSafetyEvalRequired(const Position * position,
                                          const Color color)
{
   const Color oppColor = opponent(color);

   return (bool) (numberOfNonPawnPieces(position, oppColor) >= 3 &&
                  position->piecesOfType[QUEEN | oppColor] != EMPTY_BITBOARD);
}

Bitboard randomBitboard()
{
   Bitboard tmp1 = rand();
   Bitboard tmp2 = rand();
   Bitboard tmp3 = rand();
   Bitboard tmp4 = rand();

   return tmp1 + (tmp2 << 16) + (tmp3 << 32) + (tmp4 << 48);
}

INLINE static void initializeEvaluationBase(EvaluationBase * base,
                                            KingSafetyHashInfo *
                                            kingsafetyHashtable)
{
#ifndef NDEBUG
   base->attackedSquares[WHITE] = randomBitboard();
   base->attackedSquares[BLACK] = randomBitboard();
   base->queenAttackedSquares[WHITE] = randomBitboard();
   base->queenAttackedSquares[BLACK] = randomBitboard();
   base->balance = rand();
   base->candidatePawns[WHITE] = randomBitboard();
   base->candidatePawns[BLACK] = randomBitboard();
   base->countedSquares[WHITE] = randomBitboard();
   base->countedSquares[BLACK] = randomBitboard();
   base->doubledPawns[WHITE] = randomBitboard();
   base->doubledPawns[BLACK] = randomBitboard();
   base->downwardRealm[WHITE] = randomBitboard();
   base->downwardRealm[BLACK] = randomBitboard();
   base->evaluateKingSafety[WHITE] = (bool) rand();
   base->evaluateKingSafety[BLACK] = (bool) rand();
   base->fixedPawns[WHITE] = randomBitboard();
   base->fixedPawns[BLACK] = randomBitboard();
   base->hasPassersOrCandidates[WHITE] = (bool) rand();
   base->hasPassersOrCandidates[BLACK] = (bool) rand();
   base->hiddenCandidatePawns[WHITE] = randomBitboard();
   base->hiddenCandidatePawns[BLACK] = randomBitboard();
   base->kingAttackSquares[WHITE] = randomBitboard();
   base->kingAttackSquares[BLACK] = randomBitboard();
   base->numPieceAttackers[WHITE] = rand();
   base->numPieceAttackers[BLACK] = rand();
   base->passedPawns[WHITE] = randomBitboard();
   base->passedPawns[BLACK] = randomBitboard();
   base->pawnAttackableSquares[WHITE] = randomBitboard();
   base->pawnAttackableSquares[BLACK] = randomBitboard();
   base->pawnDarkSquareMalus[WHITE] = rand();
   base->pawnDarkSquareMalus[BLACK] = rand();
   base->pawnLightSquareMalus[WHITE] = rand();
   base->pawnLightSquareMalus[BLACK] = rand();
   base->pawnProtectedSquares[WHITE] = randomBitboard();
   base->pawnProtectedSquares[BLACK] = randomBitboard();
   base->spaceAttackPoints[WHITE] = rand();
   base->spaceAttackPoints[BLACK] = rand();
   base->unprotectedPieces[WHITE] = randomBitboard();
   base->unprotectedPieces[BLACK] = randomBitboard();
   base->upwardRealm[WHITE] = randomBitboard();
   base->upwardRealm[BLACK] = randomBitboard();
   base->weakOutpostSquares[WHITE] = randomBitboard();
   base->weakOutpostSquares[BLACK] = randomBitboard();
   base->weakPawns[WHITE] = randomBitboard();
   base->weakPawns[BLACK] = randomBitboard();
   base->kingSquaresAttackCount[WHITE] = rand();
   base->kingSquaresAttackCount[BLACK] = rand();
   base->attackInfo[WHITE] = rand();
   base->attackInfo[BLACK] = rand();
#endif
   base->balance = 0;
   base->kingsafetyHashtable = kingsafetyHashtable;
   base->futilityMargin[WHITE] = base->futilityMargin[BLACK] = 0;
}

INLINE static void evaluatePieces(const Position * position,
                                  EvaluationBase * base)
{
   const Bitboard exclude = position->piecesOfType[WHITE_PAWN] |
      position->piecesOfType[BLACK_PAWN] |
      minValue[position->king[WHITE]] | minValue[position->king[BLACK]];
   Bitboard pieces = position->allPieces & (~exclude);
   Square square;

#ifndef NDEBUG
   if (debugEval)
   {
      logDebug("\nStart of piece evaluation\n");
   }
#endif

   ITERATE_BITBOARD(&pieces, square)
   {
#ifndef NDEBUG
      if (debugEval)
      {
         dumpSquare(square);
         logDebug("op=%d eg=%d\n", getOpeningValue(base->balance),
                  getEndgameValue(base->balance));
      }
#endif

      switch (position->piece[square])
      {
      case WHITE_QUEEN:
         evaluateWhiteQueen(position, base, square);
         break;

      case BLACK_QUEEN:
         evaluateBlackQueen(position, base, square);
         break;

      case WHITE_ROOK:
         evaluateWhiteRook(position, base, square);
         break;

      case BLACK_ROOK:
         evaluateBlackRook(position, base, square);
         break;

      case WHITE_BISHOP:
         evaluateWhiteBishop(position, base, square);
         break;

      case BLACK_BISHOP:
         evaluateBlackBishop(position, base, square);
         break;

      case WHITE_KNIGHT:
         evaluateWhiteKnight(position, base, square);
         break;

      case BLACK_KNIGHT:
         evaluateBlackKnight(position, base, square);
         break;
      }

#ifndef NDEBUG
      if (debugEval)
      {
         logDebug("op=%d eg=%d\n", getOpeningValue(base->balance),
                  getEndgameValue(base->balance));
      }
#endif
   }

   if (position->piecesOfType[WHITE_BISHOP] != EMPTY_BITBOARD)
   {
      evaluateWhiteTrappedBishops(position, base);
   }

   if (position->piecesOfType[BLACK_BISHOP] != EMPTY_BITBOARD)
   {
      evaluateBlackTrappedBishops(position, base);
   }

#ifndef NDEBUG
   if (debugEval)
   {
      logDebug("Values after piece evaluation\n");
      logDebug("op=%d eg=%d\n", getOpeningValue(base->balance),
               getEndgameValue(base->balance));
      logDebug("End of piece evaluation\n");
   }
#endif
}

INLINE Bitboard getFixedPawns(const Position * position,
                              const EvaluationBase * base, const Color color)
{
   const Color oppColor = opponent(color);
   const Bitboard candidates = position->piecesOfType[PAWN | color] &
      ~base->pawnProtectedSquares[color];
   const Bitboard blockers = position->piecesOfType[PAWN | color] |
      position->piecesOfColor[oppColor] |
      base->pawnProtectedSquares[oppColor];

   return candidates & (color == WHITE ? blockers >> 8 : blockers << 8);
}

INLINE bool evaluateKingChase(const Position * position,
                              const Color kingColor)
{
   if (position->numberOfPawns[WHITE] == 0 &&
       position->numberOfPawns[BLACK] == 0 &&
       numberOfNonPawnPieces(position, kingColor) <= 2)
   {
      return (bool) (getPieceWeight(position, kingColor) <
                     getPieceWeight(position, opponent(kingColor)));
   }

   return FALSE;
}

INLINE int getKingChaseMalus(const Position * position,
                             const Color huntedKingColor)
{
   const Color attackingColor = opponent(huntedKingColor);
   const Square huntedKingSquare = position->king[huntedKingColor];
   const Square attackingKingSquare = position->king[attackingColor];
   const int mutualDistance =
      distance(huntedKingSquare, attackingKingSquare) - 2;
   int cornerDistanceMalus = kingChaseMalus[ALL][huntedKingSquare];

   if (hasOrthoPieces(position, attackingColor) == FALSE &&
       getPieceCount(position, (Piece) (BISHOP | attackingColor)) > 0)
   {
      const Bitboard attackingBishops =
         position->piecesOfType[BISHOP | attackingColor];

      if ((lightSquares & attackingBishops) == EMPTY_BITBOARD)
      {
         cornerDistanceMalus = kingChaseMalus[DARK][huntedKingSquare];
      }

      if ((darkSquares & attackingBishops) == EMPTY_BITBOARD)
      {
         cornerDistanceMalus = kingChaseMalus[LIGHT][huntedKingSquare];
      }
   }

   return 5 * (5 - mutualDistance) + 15 * cornerDistanceMalus;
}

#ifdef HOMELAND_SECURITY_BONUS
int getHomelandSecurityCount(const Position * position, EvaluationBase * base,
                             const Color color)
{
   const Color oppColor = opponent(color);
   const Bitboard ownPawns = position->piecesOfType[PAWN | color];
   const Bitboard exclude = ownPawns |
      base->pawnProtectedSquares[oppColor] |
      (base->attackedSquares[oppColor] & ~base->attackedSquares[color]);
   const Bitboard safeSquares = homeland[color] & ~exclude;
   const Bitboard superSafeSquares =
      safeSquares & (color == WHITE ? (ownPawns >> 8) | (ownPawns >> 16) :
                     (ownPawns << 8) | (ownPawns << 16));

   return getNumberOfSetSquares(safeSquares) +
      getNumberOfSetSquares(superSafeSquares);
}
#endif

INLINE static void evaluatePawnMobility(const Position * position,
                                        EvaluationBase * base)
{
   const int malus = V(3, 10);
   const Bitboard whiteBlocked = (position->allPieces >> 8) &
      position->piecesOfType[WHITE_PAWN];
   const Bitboard blackBlocked = (position->allPieces << 8) &
      position->piecesOfType[BLACK_PAWN];

   base->balance -= (getNumberOfSetSquares(whiteBlocked) -
                     getNumberOfSetSquares(blackBlocked)) * malus;
}

INLINE static void getPositionalValue(const Position * position,
                                      EvaluationBase * base)
{
   const Bitboard whiteCorona = getKingMoves(position->king[WHITE]);
   const Bitboard blackCorona = getKingMoves(position->king[BLACK]);

   base->countedSquares[WHITE] = ~(position->piecesOfColor[WHITE] |
                                   base->pawnProtectedSquares[BLACK]);
   base->unprotectedPieces[WHITE] = position->piecesOfColor[WHITE] &
      ~base->pawnProtectedSquares[WHITE];
   base->countedSquares[BLACK] = ~(position->piecesOfColor[BLACK] |
                                   base->pawnProtectedSquares[WHITE]);
   base->unprotectedPieces[BLACK] = position->piecesOfColor[BLACK] &
      ~base->pawnProtectedSquares[BLACK];
   base->numPieceAttackers[WHITE] = base->numPieceAttackers[BLACK] = 0;
   base->spaceAttackPoints[WHITE] = base->spaceAttackPoints[BLACK] = 0;
   base->attackedSquares[WHITE] = base->pawnProtectedSquares[WHITE];
   base->attackedSquares[BLACK] = base->pawnProtectedSquares[BLACK];
   base->batteryAttackers[WHITE] = EMPTY_BITBOARD;
   base->batteryAttackers[BLACK] = EMPTY_BITBOARD;
   base->knightAttackedSquares[WHITE] = EMPTY_BITBOARD;
   base->knightAttackedSquares[BLACK] = EMPTY_BITBOARD;
   base->bishopAttackedSquares[WHITE] = EMPTY_BITBOARD;
   base->bishopAttackedSquares[BLACK] = EMPTY_BITBOARD;
   base->rookAttackedSquares[WHITE] = EMPTY_BITBOARD;
   base->rookAttackedSquares[BLACK] = EMPTY_BITBOARD;
   base->queenAttackedSquares[WHITE] = EMPTY_BITBOARD;
   base->queenAttackedSquares[BLACK] = EMPTY_BITBOARD;
   base->kingAttackSquares[WHITE] = whiteCorona | (whiteCorona << 8);
   base->kingAttackSquares[BLACK] = blackCorona | (blackCorona >> 8);
   base->evaluateKingSafety[WHITE] = kingSafetyEvalRequired(position, WHITE);
   base->evaluateKingSafety[BLACK] = kingSafetyEvalRequired(position, BLACK);
   base->kingSquaresAttackCount[WHITE] =
      base->kingSquaresAttackCount[BLACK] = 0;
   base->attackInfo[WHITE] = base->attackInfo[BLACK] = 0;

   evaluatePieces(position, base);
   base->attackedSquares[WHITE] |= base->knightAttackedSquares[WHITE] |
      base->bishopAttackedSquares[WHITE] | base->rookAttackedSquares[WHITE];
   base->attackedSquares[BLACK] |= base->knightAttackedSquares[BLACK] |
      base->bishopAttackedSquares[BLACK] | base->rookAttackedSquares[BLACK];

#ifndef NDEBUG
   if (debugEval)
   {
      logDebug("\nEval values before king safety eval:\n");
      logDebug("op=%d eg=%d\n", getOpeningValue(base->balance),
               getEndgameValue(base->balance));
   }
#endif

   {
      const int malus = getKingSafetyMalus(position, base, WHITE);

      base->balance -= V(malus, 0);
      base->futilityMargin[WHITE] = max(base->futilityMargin[WHITE], malus);
   }

   {
      const int malus = getKingSafetyMalus(position, base, BLACK);

      base->balance += V(malus, 0);
      base->futilityMargin[BLACK] = max(base->futilityMargin[BLACK], malus);
   }

   base->attackedSquares[WHITE] |= base->queenAttackedSquares[WHITE];
   base->attackedSquares[BLACK] |= base->queenAttackedSquares[BLACK];

#ifdef MALUS_PAWN_MOBILITY
   evaluatePawnMobility(position, base);
#endif

#ifndef NDEBUG
   if (debugEval)
   {
      logDebug("\nEval values before passed pawn eval:\n");
      logDebug("op=%d eg=%d\n", getOpeningValue(base->balance),
               getEndgameValue(base->balance));
   }
#endif

   if (base->passedPawns[WHITE] != EMPTY_BITBOARD ||
       base->passedPawns[BLACK] != EMPTY_BITBOARD)
   {
#ifndef NDEBUG
      if (debugEval)
      {
         dumpBitboard(base->passedPawns[WHITE], "white passers");
         dumpBitboard(base->passedPawns[BLACK], "black passers");
      }
#endif

      evaluatePassedPawns(position, base);
   }

   if (evaluateKingChase(position, WHITE))
   {
      const int kingChaseMalus = getKingChaseMalus(position, WHITE);

      base->balance -= V(kingChaseMalus, kingChaseMalus);
   }
   else if (evaluateKingChase(position, BLACK))
   {
      const int kingChaseMalus = getKingChaseMalus(position, BLACK);

      base->balance += V(kingChaseMalus, kingChaseMalus);
   }

#ifndef NDEBUG
   if (debugEval)
   {
      logDebug("\nEval values before space attack eval:\n");
      logDebug("op=%d eg=%d\n", getOpeningValue(base->balance),
               getEndgameValue(base->balance));
   }
#endif

#ifdef BONUS_SPACE_ATTACKS
   {
      const Bitboard whiteAttackers = position->piecesOfColor[WHITE] &
         attackingRealm[WHITE];
      const Bitboard blackAttackers = position->piecesOfColor[BLACK] &
         attackingRealm[BLACK];
      const int numWhiteAttackers = getNumberOfSetSquares(whiteAttackers);
      const int numBlackAttackers = getNumberOfSetSquares(blackAttackers);
      const int bonus =
         SPACE_BONUS_WEIGHT[numWhiteAttackers] *
         (base->spaceAttackPoints[WHITE] + numWhiteAttackers) -
         SPACE_BONUS_WEIGHT[numBlackAttackers] *
         (base->spaceAttackPoints[BLACK] + numBlackAttackers);

      base->balance += V(bonus, 0);
   }
#endif

#ifdef MALUS_KING_TRAPPED
   if (whiteKingIsTrapped(position, base))
   {
      base->balance -= KING_MALUS_TRAPPED;
   }

   if (blackKingIsTrapped(position, base))
   {
      base->balance += KING_MALUS_TRAPPED;
   }
#endif

#ifdef HOMELAND_SECURITY_BONUS
   {
      const int weight = getHomeSecurityWeight(position);

      if (weight > 0)
      {
         const int countWhite =
            getHomelandSecurityCount(position, base, WHITE);
         const int countBlack =
            getHomelandSecurityCount(position, base, BLACK);

         base->balance += V(((countWhite - countBlack) * weight) / 4, 0);
      }
   }
#endif

   evaluateWhiteAttackers(base);
   evaluateBlackAttackers(base);

#ifdef BONUS_PINS
   evaluateWhitePins(position, base);
   evaluateBlackPins(position, base);
#endif

#ifndef NDEBUG
   if (debugEval)
   {
      logDebug("\nFinal eval values:\n");
      logDebug("op=%d eg=%d\n", getOpeningValue(base->balance),
               getEndgameValue(base->balance));
   }
#endif
}

int getValue(const Position * position,
             EvaluationBase * base,
             PawnHashInfo * pawnHashtable,
             KingSafetyHashInfo * kingsafetyHashtable)
{
   PawnHashInfo *pawnHashInfo =
      &pawnHashtable[position->pawnHashValue & PAWN_HASHTABLE_MASK];

   initializeEvaluationBase(base, kingsafetyHashtable);

   if (pawnHashInfo->hashValue == position->pawnHashValue &&
       pawnHashInfo->hashValue != 0)
   {
#ifndef NDEBUG
      getPawnInfo(position, base);
      evaluatePawns(position, base);

      assert(base->balance == pawnHashInfo->balance);
      assert(base->pawnProtectedSquares[WHITE] ==
             pawnHashInfo->pawnProtectedSquares[WHITE]);
      assert(base->pawnProtectedSquares[BLACK] ==
             pawnHashInfo->pawnProtectedSquares[BLACK]);
      assert(base->passedPawns[WHITE] == pawnHashInfo->passedPawns[WHITE]);
      assert(base->passedPawns[BLACK] == pawnHashInfo->passedPawns[BLACK]);
#endif

      base->balance = pawnHashInfo->balance;
      base->passedPawns[WHITE] = pawnHashInfo->passedPawns[WHITE];
      base->passedPawns[BLACK] = pawnHashInfo->passedPawns[BLACK];
      base->pawnProtectedSquares[WHITE] =
         pawnHashInfo->pawnProtectedSquares[WHITE];
      base->pawnProtectedSquares[BLACK] =
         pawnHashInfo->pawnProtectedSquares[BLACK];
      base->weakOutpostSquares[WHITE] =
         pawnHashInfo->weakOutpostSquares[WHITE];
      base->weakOutpostSquares[BLACK] =
         pawnHashInfo->weakOutpostSquares[BLACK];
      base->pawnAttackableSquares[WHITE] =
         pawnHashInfo->pawnAttackableSquares[WHITE];
      base->pawnAttackableSquares[BLACK] =
         pawnHashInfo->pawnAttackableSquares[BLACK];
      base->pawnLightSquareMalus[WHITE] =
         pawnHashInfo->pawnLightSquareMalus[WHITE];
      base->pawnLightSquareMalus[BLACK] =
         pawnHashInfo->pawnLightSquareMalus[BLACK];
      base->pawnDarkSquareMalus[WHITE] =
         pawnHashInfo->pawnDarkSquareMalus[WHITE];
      base->pawnDarkSquareMalus[BLACK] =
         pawnHashInfo->pawnDarkSquareMalus[BLACK];
#ifdef BONUS_HIDDEN_PASSER
      base->hasPassersOrCandidates[WHITE] =
         pawnHashInfo->hasPassersOrCandidates[WHITE];
      base->hasPassersOrCandidates[BLACK] =
         pawnHashInfo->hasPassersOrCandidates[BLACK];
#endif
   }
   else
   {
      getPawnInfo(position, base);
      evaluatePawns(position, base);

      pawnHashInfo->hashValue = position->pawnHashValue;
      pawnHashInfo->balance = base->balance;
      pawnHashInfo->pawnProtectedSquares[WHITE] =
         base->pawnProtectedSquares[WHITE];
      pawnHashInfo->pawnProtectedSquares[BLACK] =
         base->pawnProtectedSquares[BLACK];
      pawnHashInfo->passedPawns[WHITE] = base->passedPawns[WHITE];
      pawnHashInfo->passedPawns[BLACK] = base->passedPawns[BLACK];
      pawnHashInfo->weakOutpostSquares[WHITE] =
         base->weakOutpostSquares[WHITE];
      pawnHashInfo->weakOutpostSquares[BLACK] =
         base->weakOutpostSquares[BLACK];
      pawnHashInfo->pawnAttackableSquares[WHITE] =
         base->pawnAttackableSquares[WHITE];
      pawnHashInfo->pawnAttackableSquares[BLACK] =
         base->pawnAttackableSquares[BLACK];
      pawnHashInfo->pawnLightSquareMalus[WHITE] =
         base->pawnLightSquareMalus[WHITE];
      pawnHashInfo->pawnLightSquareMalus[BLACK] =
         base->pawnLightSquareMalus[BLACK];
      pawnHashInfo->pawnDarkSquareMalus[WHITE] =
         base->pawnDarkSquareMalus[WHITE];
      pawnHashInfo->pawnDarkSquareMalus[BLACK] =
         base->pawnDarkSquareMalus[BLACK];
#ifdef BONUS_HIDDEN_PASSER
      pawnHashInfo->hasPassersOrCandidates[WHITE] =
         base->hasPassersOrCandidates[WHITE];
      pawnHashInfo->hasPassersOrCandidates[BLACK] =
         base->hasPassersOrCandidates[BLACK];
#endif
   }

#ifdef TRACE_EVAL
   logDebug("\nStarting evaluation.\n");
   logDebug("phaseIndex = %d\n", phaseIndex(position));
   logDebug("opvWhite = %d egvWhite = %d\n",
            position->openingValue[WHITE], position->endgameValue[WHITE]);
   logDebug("opvBlack = %d egvBlack = %d\n",
            position->openingValue[BLACK], position->endgameValue[BLACK]);
   logDebug("basicValue = %d\n\n", basicValue);
   logPosition(position);
   logDebug("\n");
#endif

   if (kpkpValueAvailable(position))
   {
      int currentPositionalValue = getKpkpValue(position);

      if (currentPositionalValue == 0)
      {
         return 0;
      }

      getPositionalValue(position, base);
   }
   else
   {
      getPositionalValue(position, base);
   }

   return positionalBalance(position, base);
}

static void transposeMatrix(const int human[], int machine[])
{
   int file, rank, i = 0;

   for (rank = RANK_8; rank >= RANK_1; rank--)
   {
      for (file = FILE_A; file <= FILE_H; file++)
      {
         const Square machineSquare = getSquare(file, rank);

         machine[machineSquare] = human[i++];
      }
   }
}

#define PSV(piece,square,stage) pieceSquareValue[(piece)][(square)][(stage)]

static void initializePieceSquareValues()
{
   const int PawnFileOpening = 5;
   const int KnightCentreOpening = 5;
   const int KnightCentreEndgame = 5;
   const int KnightRankOpening = 5;
   const int KnightBackRankOpening = 0;
   const int KnightTrapped = 100;
   const int BishopCentreOpening = 2;
   const int BishopCentreEndgame = 3;
   const int BishopBackRankOpening = 10;
   const int BishopDiagonalOpening = 4;
   const int RookFileOpening = 3;
   const int QueenCentreOpening = 1;
   const int QueenCentreEndgame = 4;
   const int QueenBackRankOpening = 5;
   const int KingCentreEndgame = 12;
   const int KingFileOpening = 8;
   const int KingRankOpening = 6;
   const int PawnFile[8] = { -3, -1, +0, +1, +1, +0, -1, -3, };
   const int KnightLine[8] = { -4, -2, +0, +1, +1, +0, -2, -4, };
   const int KnightRank[8] = { -2, -1, +0, +1, +2, +3, +2, +1, };
   const int BishopLine[8] = { -3, -1, +0, +1, +1, +0, -1, -3, };
   const int RookFile[8] = { -2, -1, +0, +1, +1, +0, -1, -2, };
   const int QueenFileOpening[8] = { -2, -1, +0, +0, +0, +0, -1, -2, };
   const int QueenRankOpening[8] = { -1, +0, +0, +0, +0, +0, +0, -1, };
   const int QueenFileEndgame[8] = { -3, -1, +0, +1, +1, +0, -1, -3, };
   const int QueenRankEndgame[8] = { -3, -1, +0, +1, +1, +0, -1, -3, };
   const int KingFileEndgame[8] = { -3, -1, +0, +1, +1, +0, -1, -3, };
   const int KingRankEndgame[8] = { -3, -1, +0, +1, +1, +0, -1, -3, };
   const int KingFile[8] = { +3, +4, +2, +0, +0, +2, +4, +3, };
   const int KingRank[8] = { +1, +0, -2, -3, -4, -5, -6, -7, };
   int i;
   Square sq;

   for (i = 0; i < 16; i++)
   {
      int j;

      for (j = 0; j < _64_; j++)
      {
         pieceSquareValue[i][j][0] = pieceSquareValue[i][j][1] = 0;
      }
   }

   /* pawns */

   ITERATE(sq)
   {
      PSV(WHITE_PAWN, sq, OPENING) += PawnFile[file(sq)] * PawnFileOpening;
      PSV(WHITE_PAWN, sq, OPENING) += PAWN_VALUE_OPENING;
      PSV(WHITE_PAWN, sq, ENDGAME) += PAWN_VALUE_ENDGAME;
   }

   PSV(WHITE_PAWN, D3, OPENING) += 10;
   PSV(WHITE_PAWN, E3, OPENING) += 10;
   PSV(WHITE_PAWN, D4, OPENING) += 20;
   PSV(WHITE_PAWN, E4, OPENING) += 20;
   PSV(WHITE_PAWN, D5, OPENING) += 10;
   PSV(WHITE_PAWN, E5, OPENING) += 10;

   /* knights */

   ITERATE(sq)
   {
      PSV(WHITE_KNIGHT, sq, OPENING) += VALUE_KNIGHT_OPENING;
      PSV(WHITE_KNIGHT, sq, ENDGAME) += VALUE_KNIGHT_ENDGAME;
      PSV(WHITE_KNIGHT, sq, OPENING) +=
         KnightLine[file(sq)] * KnightCentreOpening;
      PSV(WHITE_KNIGHT, sq, OPENING) +=
         KnightLine[rank(sq)] * KnightCentreOpening;
      PSV(WHITE_KNIGHT, sq, ENDGAME) +=
         KnightLine[file(sq)] * KnightCentreEndgame;
      PSV(WHITE_KNIGHT, sq, ENDGAME) +=
         KnightLine[rank(sq)] * KnightCentreEndgame;
      PSV(WHITE_KNIGHT, sq, OPENING) +=
         KnightRank[rank(sq)] * KnightRankOpening;
   }

   for (sq = A1; sq <= H1; sq++)
   {
      PSV(WHITE_KNIGHT, sq, OPENING) -= KnightBackRankOpening;
   }

   PSV(WHITE_KNIGHT, A8, OPENING) -= KnightTrapped;
   PSV(WHITE_KNIGHT, H8, OPENING) -= KnightTrapped;

   /* bishops */

   ITERATE(sq)
   {
      PSV(WHITE_BISHOP, sq, OPENING) += VALUE_BISHOP_OPENING;
      PSV(WHITE_BISHOP, sq, ENDGAME) += VALUE_BISHOP_ENDGAME;
      PSV(WHITE_BISHOP, sq, OPENING) +=
         BishopLine[file(sq)] * BishopCentreOpening;
      PSV(WHITE_BISHOP, sq, OPENING) +=
         BishopLine[rank(sq)] * BishopCentreOpening;
      PSV(WHITE_BISHOP, sq, ENDGAME) +=
         BishopLine[file(sq)] * BishopCentreEndgame;
      PSV(WHITE_BISHOP, sq, ENDGAME) +=
         BishopLine[rank(sq)] * BishopCentreEndgame;
   }

   for (sq = A1; sq <= H1; sq++)
   {
      PSV(WHITE_BISHOP, sq, OPENING) -= BishopBackRankOpening;
   }

   for (i = 0; i < 8; i++)
   {
      sq = getSquare(i, i);
      PSV(WHITE_BISHOP, sq, OPENING) += BishopDiagonalOpening;
      PSV(WHITE_BISHOP, getFlippedSquare(sq), OPENING) +=
         BishopDiagonalOpening;
   }

   /* rooks */

   ITERATE(sq)
   {
      PSV(WHITE_ROOK, sq, OPENING) += VALUE_ROOK_OPENING;
      PSV(WHITE_ROOK, sq, ENDGAME) += VALUE_ROOK_ENDGAME;
      PSV(WHITE_ROOK, sq, OPENING) += RookFile[file(sq)] * RookFileOpening;
   }

   /* queens */

   ITERATE(sq)
   {
      PSV(WHITE_QUEEN, sq, OPENING) += VALUE_QUEEN_OPENING;
      PSV(WHITE_QUEEN, sq, ENDGAME) += VALUE_QUEEN_ENDGAME;
      PSV(WHITE_QUEEN, sq, OPENING) +=
         QueenFileOpening[file(sq)] * QueenCentreOpening;
      PSV(WHITE_QUEEN, sq, OPENING) +=
         QueenRankOpening[rank(sq)] * QueenCentreOpening;
      PSV(WHITE_QUEEN, sq, ENDGAME) +=
         QueenFileEndgame[file(sq)] * QueenCentreEndgame;
      PSV(WHITE_QUEEN, sq, ENDGAME) +=
         QueenRankEndgame[rank(sq)] * QueenCentreEndgame;
   }

   for (sq = A1; sq <= H1; sq++)
   {
      PSV(WHITE_QUEEN, sq, OPENING) -= QueenBackRankOpening;
   }

   /* king */

   ITERATE(sq)
   {
      PSV(WHITE_KING, sq, ENDGAME) +=
         KingFileEndgame[file(sq)] * KingCentreEndgame;
      PSV(WHITE_KING, sq, ENDGAME) +=
         KingRankEndgame[rank(sq)] * KingCentreEndgame;
      PSV(WHITE_KING, sq, OPENING) += KingFile[file(sq)] * KingFileOpening;
      PSV(WHITE_KING, sq, OPENING) += KingRank[rank(sq)] * KingRankOpening;
   }

#ifdef PST_TABLES
   ITERATE(sq)
   {
      PSV(WHITE_PAWN, sq, OPENING) =
         PAWN_DEFAULTVALUE_OPENING + (SF_STP_OP[0][sq] * 100) / 256;
      PSV(WHITE_PAWN, sq, ENDGAME) =
         PAWN_DEFAULTVALUE_ENDGAME + (SF_STP_EG[0][sq] * 100) / 256;

      PSV(WHITE_KNIGHT, sq, OPENING) =
         VALUE_KNIGHT_OPENING + (SF_STP_OP[1][sq] * 100) / 256;
      PSV(WHITE_KNIGHT, sq, ENDGAME) =
         VALUE_KNIGHT_ENDGAME + (SF_STP_EG[1][sq] * 100) / 256;

      PSV(WHITE_BISHOP, sq, OPENING) =
         VALUE_BISHOP_OPENING + (SF_STP_OP[2][sq] * 100) / 256;
      PSV(WHITE_BISHOP, sq, ENDGAME) =
         VALUE_BISHOP_ENDGAME + (SF_STP_EG[2][sq] * 100) / 256;

      PSV(WHITE_ROOK, sq, OPENING) =
         VALUE_ROOK_OPENING + (SF_STP_OP[3][sq] * 100) / 256;
      PSV(WHITE_ROOK, sq, ENDGAME) =
         VALUE_ROOK_ENDGAME + (SF_STP_EG[3][sq] * 100) / 256;

      PSV(WHITE_QUEEN, sq, OPENING) =
         VALUE_QUEEN_OPENING + (SF_STP_OP[4][sq] * 100) / 256;
      PSV(WHITE_QUEEN, sq, ENDGAME) =
         VALUE_QUEEN_ENDGAME + (SF_STP_EG[4][sq] * 100) / 256;

      PSV(WHITE_KING, sq, OPENING) = (SF_STP_OP[5][sq] * 100) / 256;
      PSV(WHITE_KING, sq, ENDGAME) = (SF_STP_EG[5][sq] * 100) / 256;
   }
#endif

   /* Set the value of the black pieces. */

   ITERATE(sq)
   {
      int stage;

      for (stage = OPENING; stage <= ENDGAME; stage++)
      {
         pieceSquareValue[BLACK_KING][sq][stage] =
            pieceSquareValue[WHITE_KING][getFlippedSquare(sq)][stage];
         pieceSquareValue[BLACK_QUEEN][sq][stage] =
            pieceSquareValue[WHITE_QUEEN][getFlippedSquare(sq)][stage];
         pieceSquareValue[BLACK_ROOK][sq][stage] =
            pieceSquareValue[WHITE_ROOK][getFlippedSquare(sq)][stage];
         pieceSquareValue[BLACK_BISHOP][sq][stage] =
            pieceSquareValue[WHITE_BISHOP][getFlippedSquare(sq)][stage];
         pieceSquareValue[BLACK_KNIGHT][sq][stage] =
            pieceSquareValue[WHITE_KNIGHT][getFlippedSquare(sq)][stage];
         pieceSquareValue[BLACK_PAWN][sq][stage] =
            pieceSquareValue[WHITE_PAWN][getFlippedSquare(sq)][stage];
      }
   }

   ITERATE(sq)
   {
      int piece;

      for (piece = 0; piece < 16; piece++)
      {
         pieceSquareBonus[piece][sq] =
            V(pieceSquareValue[piece][sq][OPENING],
              pieceSquareValue[piece][sq][ENDGAME]);
      }
   }
}

void initializeKingAttacks()
{
   Square square;

   ITERATE(square)
   {
      const Bitboard corona = getKingMoves(square);
      KingAttacks *attackInfo = &kingAttacks[square];
      Square attackerSquare;

      attackInfo->diaAttackers = attackInfo->orthoAttackers =
         attackInfo->knightAttackers = attackInfo->pawnAttackers[WHITE] =
         attackInfo->pawnAttackers[WHITE] = EMPTY_BITBOARD;

      ITERATE(attackerSquare)
      {
         attackInfo->attackedByDia[attackerSquare] =
            attackInfo->attackedByOrtho[attackerSquare] = NO_SQUARE;
      }

      ITERATE(attackerSquare)
      {
         Bitboard dia, ortho;
         const Bitboard knight =
            corona & generalMoves[WHITE_KNIGHT][attackerSquare];
         const Bitboard whitePawn =
            corona & generalMoves[WHITE_PAWN][attackerSquare];
         const Bitboard blackPawn =
            corona & generalMoves[BLACK_PAWN][attackerSquare];

         dia = corona & generalMoves[WHITE_BISHOP][attackerSquare];
         ortho = corona & generalMoves[WHITE_ROOK][attackerSquare];

         if (dia != EMPTY_BITBOARD)
         {
            Square attackedSquare;
            int dist = 8;

            setSquare(attackInfo->diaAttackers, attackerSquare);

            ITERATE_BITBOARD(&dia, attackedSquare)
            {
               const int currentDistance =
                  distance(attackerSquare, attackedSquare);

               if (currentDistance < dist)
               {
                  attackInfo->attackedByDia[attackerSquare] = attackedSquare;
                  dist = currentDistance;
               }
            }
         }

         if (ortho != EMPTY_BITBOARD)
         {
            Square attackedSquare;
            int dist = 8;

            setSquare(attackInfo->orthoAttackers, attackerSquare);

            ITERATE_BITBOARD(&ortho, attackedSquare)
            {
               const int currentDistance =
                  distance(attackerSquare, attackedSquare);

               if (currentDistance < dist)
               {
                  attackInfo->attackedByOrtho[attackerSquare] =
                     attackedSquare;
                  dist = currentDistance;
               }
            }
         }

         if (knight != EMPTY_BITBOARD)
         {
            setSquare(attackInfo->knightAttackers, attackerSquare);
         }

         if (whitePawn != EMPTY_BITBOARD)
         {
            setSquare(attackInfo->pawnAttackers[WHITE], attackerSquare);
         }

         if (blackPawn != EMPTY_BITBOARD)
         {
            setSquare(attackInfo->pawnAttackers[BLACK], attackerSquare);
         }
      }
   }
}

static void getPieceTradeSignatures(UINT32 * materialSignatureWhite,
                                    UINT32 * materialSignatureBlack)
{
   int numWhiteQueens, numWhiteRooks, numWhiteLightSquareBishops;
   int numWhiteDarkSquareBishops, numWhiteKnights, numWhitePawns;
   int numBlackQueens, numBlackRooks, numBlackLightSquareBishops;
   int numBlackDarkSquareBishops, numBlackKnights, numBlackPawns;

   getPieceCounters(*materialSignatureWhite, &numWhiteQueens, &numWhiteRooks,
                    &numWhiteLightSquareBishops, &numWhiteDarkSquareBishops,
                    &numWhiteKnights, &numWhitePawns);
   getPieceCounters(*materialSignatureBlack, &numBlackQueens, &numBlackRooks,
                    &numBlackLightSquareBishops, &numBlackDarkSquareBishops,
                    &numBlackKnights, &numBlackPawns);

   while (numWhiteQueens > 0 && numBlackQueens > 0)
   {
      numWhiteQueens--;
      numBlackQueens--;
   }

   while (numWhiteRooks > 0 && numBlackRooks > 0)
   {
      numWhiteRooks--;
      numBlackRooks--;
   }

   while (numWhiteLightSquareBishops > 0 && numBlackLightSquareBishops > 0)
   {
      numWhiteLightSquareBishops--;
      numBlackLightSquareBishops--;
   }

   while (numWhiteDarkSquareBishops > 0 && numBlackDarkSquareBishops > 0)
   {
      numWhiteDarkSquareBishops--;
      numBlackDarkSquareBishops--;
   }

   while (numWhiteLightSquareBishops > 0 && numBlackDarkSquareBishops > 0)
   {
      numWhiteLightSquareBishops--;
      numBlackDarkSquareBishops--;
   }

   while (numWhiteDarkSquareBishops > 0 && numBlackLightSquareBishops > 0)
   {
      numWhiteDarkSquareBishops--;
      numBlackLightSquareBishops--;
   }

   while (numWhiteKnights > 0 && numBlackKnights > 0)
   {
      numWhiteKnights--;
      numBlackKnights--;
   }

   while (numWhiteLightSquareBishops > 0 && numBlackKnights > 0 &&
          numWhiteDarkSquareBishops == 0)
   {
      numWhiteLightSquareBishops--;
      numBlackKnights--;
   }

   while (numWhiteDarkSquareBishops > 0 && numBlackKnights > 0 &&
          numWhiteLightSquareBishops == 0)
   {
      numWhiteDarkSquareBishops--;
      numBlackKnights--;
   }

   while (numWhiteKnights > 0 && numBlackLightSquareBishops > 0 &&
          numBlackDarkSquareBishops == 0)
   {
      numWhiteKnights--;
      numBlackLightSquareBishops--;
   }

   while (numWhiteKnights > 0 && numBlackDarkSquareBishops > 0 &&
          numBlackLightSquareBishops == 0)
   {
      numWhiteKnights--;
      numBlackDarkSquareBishops--;
   }

   *materialSignatureWhite =
      materialSignature(numWhiteQueens, numWhiteRooks,
                        numWhiteLightSquareBishops, numWhiteDarkSquareBishops,
                        numWhiteKnights, numWhitePawns);
   *materialSignatureBlack =
      materialSignature(numBlackQueens, numBlackRooks,
                        numBlackLightSquareBishops, numBlackDarkSquareBishops,
                        numBlackKnights, numBlackPawns);
}

static bool hasMaterialForMate(const UINT32 materialSignature,
                               const UINT32 oppMaterialSignature,
                               SpecialEvalType * specialEval,
                               const bool tradePieces)
{
   int numQueens, numRooks, numLightSquareBishops, numDarkSquareBishops;
   int numKnights, numPawns;
   int numOppQueens, numOppRooks, numOppLightSquareBishops;
   int numOppDarkSquareBishops, numOppKnights, numOppPawns;
   int numBishops, numOppBishops;

   if (tradePieces)
   {
      UINT32 ms = materialSignature, mso = oppMaterialSignature;
      SpecialEvalType dummy;

      getPieceTradeSignatures(&ms, &mso);

      return hasMaterialForMate(ms, mso, &dummy, FALSE);
   }

   getPieceCounters(materialSignature, &numQueens, &numRooks,
                    &numLightSquareBishops, &numDarkSquareBishops,
                    &numKnights, &numPawns);
   getPieceCounters(oppMaterialSignature, &numOppQueens, &numOppRooks,
                    &numOppLightSquareBishops, &numOppDarkSquareBishops,
                    &numOppKnights, &numOppPawns);

   numBishops = numLightSquareBishops + numDarkSquareBishops;
   numOppBishops = numOppLightSquareBishops + numOppDarkSquareBishops;

   if (numQueens + numRooks + numLightSquareBishops + numDarkSquareBishops +
       numKnights == 0 && numPawns > 0)
   {
      *specialEval = Se_KpK;

      return TRUE;
   }

   if (numQueens + numRooks + numLightSquareBishops + numDarkSquareBishops +
       numPawns == 0 && numKnights == 2)
   {
      if (numOppQueens + numOppRooks + numOppLightSquareBishops +
          numOppDarkSquareBishops + numOppKnights == 0 && numOppPawns > 0)
      {
         *specialEval = Se_KnnKp;

         return TRUE;
      }
   }

   if (numQueens + numRooks + numKnights == 0 &&
       numLightSquareBishops + numDarkSquareBishops == 1 && numPawns > 0)
   {
      *specialEval = Se_KbpK;

      return TRUE;
   }

   if (numQueens + numBishops + numKnights == 0 && numRooks == 1 &&
       numPawns == 1 && numOppBishops >= 1)
   {
      *specialEval = Se_KrpKb;

      return TRUE;
   }

   if (numQueens + numBishops + numKnights == 0 && numRooks == 1 &&
       numPawns == 1 && numOppRooks >= 1)
   {
      *specialEval = Se_KrpKr;

      return TRUE;
   }

   if (numQueens + numBishops + numKnights == 0 && numRooks == 1 &&
       numPawns == 2 && numOppRooks >= 1)
   {
      *specialEval = Se_KrppKr;

      return TRUE;
   }

   if (numRooks + numBishops + numKnights == 0 && numQueens == 1 &&
       numPawns == 1 && numOppQueens >= 1)
   {
      *specialEval = Se_KqpKq;

      return TRUE;
   }

   if (numRooks + numBishops + numKnights == 0 && numQueens == 1 &&
       numPawns == 2 && numOppQueens >= 1)
   {
      *specialEval = Se_KqppKq;

      return TRUE;
   }

   if (numQueens + numRooks + numPawns > 0 || numKnights >= 3)
   {
      return TRUE;
   }

   if (numLightSquareBishops > 0 && numDarkSquareBishops > 0)
   {
      return TRUE;
   }

   if (numKnights > 0 && numLightSquareBishops + numDarkSquareBishops > 0)
   {
      return TRUE;
   }

   return FALSE;
}

static PieceType getKamikazePiece(const UINT32 ownMaterialSignature,
                                  const UINT32 oppMaterialSignature)
{
   int numQueens, numRooks, numLightSquareBishops;
   int numDarkSquareBishops, numKnights, numPawns;
   int numOppQueens, numOppRooks, numOppLightSquareBishops;
   int numOppDarkSquareBishops, numOppKnights, numOppPawns;
   int ownSignature;

   getPieceCounters(ownMaterialSignature, &numQueens, &numRooks,
                    &numLightSquareBishops, &numDarkSquareBishops,
                    &numKnights, &numPawns);
   getPieceCounters(oppMaterialSignature, &numOppQueens, &numOppRooks,
                    &numOppLightSquareBishops, &numOppDarkSquareBishops,
                    &numOppKnights, &numOppPawns);

   ownSignature =
      materialSignature(numQueens, numRooks,
                        numLightSquareBishops,
                        numDarkSquareBishops, numKnights, numPawns - 1);

   if (numOppRooks > 0)
   {
      const int oppSignature =
         materialSignature(numOppQueens, numOppRooks - 1,
                           numOppLightSquareBishops,
                           numOppDarkSquareBishops,
                           numOppKnights, numOppPawns);

      if (hasMaterialForMate(ownSignature, oppSignature, 0, TRUE) == FALSE)
      {
         return ROOK;
      }
   }

   if (numOppLightSquareBishops > 0)
   {
      const int oppSignature = materialSignature(numOppQueens, numOppRooks,
                                                 numOppLightSquareBishops - 1,
                                                 numOppDarkSquareBishops,
                                                 numOppKnights, numOppPawns);

      if (hasMaterialForMate(ownSignature, oppSignature, 0, TRUE) == FALSE)
      {
         return BISHOP;
      }
   }

   if (numOppDarkSquareBishops > 0)
   {
      const int oppSignature = materialSignature(numOppQueens, numOppRooks,
                                                 numOppLightSquareBishops,
                                                 numOppDarkSquareBishops - 1,
                                                 numOppKnights, numOppPawns);

      if (hasMaterialForMate(ownSignature, oppSignature, 0, TRUE) == FALSE)
      {
         return BISHOP;
      }
   }

   if (numOppKnights > 0)
   {
      const int oppSignature = materialSignature(numOppQueens, numOppRooks,
                                                 numOppLightSquareBishops,
                                                 numOppDarkSquareBishops,
                                                 numOppKnights - 1,
                                                 numOppPawns);

      if (hasMaterialForMate(ownSignature, oppSignature, 0, TRUE) == FALSE)
      {
         return KNIGHT;
      }
   }

   return NO_PIECETYPE;
}

static UINT8 getWinningChances(const UINT32 materialSignature,
                               const UINT32 oppMaterialSignature)
{
   int numQueens, numRooks, numLightSquareBishops;
   int numDarkSquareBishops, numKnights, numPawns;
   int numOppQueens, numOppRooks, numOppLightSquareBishops;
   int numOppDarkSquareBishops, numOppKnights, numOppPawns;
   int numPieces, numOppPieces;
   int numOppBishops, numOppMinors, numOppSliders;
   bool oppositeColoredBishops;
   PieceType kamikazePiece = getKamikazePiece(materialSignature,
                                              oppMaterialSignature);

   getPieceCounters(materialSignature, &numQueens, &numRooks,
                    &numLightSquareBishops, &numDarkSquareBishops,
                    &numKnights, &numPawns);
   getPieceCounters(oppMaterialSignature, &numOppQueens, &numOppRooks,
                    &numOppLightSquareBishops, &numOppDarkSquareBishops,
                    &numOppKnights, &numOppPawns);
   numPieces = numQueens + numRooks + numLightSquareBishops +
      numDarkSquareBishops + numKnights;
   numOppBishops = numOppLightSquareBishops + numOppDarkSquareBishops;
   numOppMinors = numOppBishops + numOppKnights;
   numOppSliders = numOppQueens + numOppRooks + numOppBishops;
   numOppPieces = numOppSliders + numOppKnights;
   oppositeColoredBishops = (bool)
      (numLightSquareBishops + numDarkSquareBishops > 0 &&
       ((numOppLightSquareBishops > 0 && numLightSquareBishops == 0) ||
        (numOppDarkSquareBishops > 0 && numDarkSquareBishops == 0)));

#ifdef KAMIKAZE_LIMITATION
   if (numOppPieces == 1 && numOppLightSquareBishops +
       numOppDarkSquareBishops + numOppKnights == 1 &&
       numPieces > numOppPieces)
   {
      kamikazePiece = NO_PIECE;
   }
#endif

   if (numPieces == 0)
   {
      if (numPawns <= 1 && numOppSliders > 0)
      {
         return 0;
      }

      if (numPawns <= 1 && numOppKnights > 0)
      {
         return 4;
      }

      if (numPawns == 2)
      {
         return (numOppSliders >= 2 ? 2 : 8);
      }
   }

   if (numPieces == 1)
   {
      if (oppositeColoredBishops)
      {
         return (numPawns > 1 ? 8 : 0);
      }

      if (numPawns == 1)        /* One piece, one pawn: */
      {
         if (numQueens > 0 && numOppRooks >= 2)
         {
            return 1;
         }

         if (numQueens > 0 && numOppRooks + numOppMinors >= 2)
         {
            return 12;          /* usually won, but difficult */
         }

         if (kamikazePiece != NO_PIECETYPE)
         {
            switch (kamikazePiece)
            {
            case ROOK:
               return 1;
            case BISHOP:
               return 2;
            case KNIGHT:
               return 4;
            default:
               break;
            }
         }
      }
   }
   else if (numPieces == 2)     /* has more than one piece: */
   {
      if (numPawns <= 1)
      {
         if (numRooks == 2 && numOppQueens > 0)
         {
            return (numPawns == 0 ? 1 : 2);
         }

         if (kamikazePiece != NO_PIECETYPE)
         {
            switch (kamikazePiece)
            {
            case ROOK:
               return 1;
            case BISHOP:
               return 2;
            case KNIGHT:
               return 4;
            default:
               break;
            }
         }
      }

      if (numQueens == 0 && numRooks <= 1 && numRooks == numOppRooks &&
          oppositeColoredBishops)
      {
         return 12;
      }
   }

   return 16;
}

static UINT8 getWinningChancesWithoutPawn(UINT32 materialSignature,
                                          UINT32 oppMaterialSignature)
{
   int numQueens, numRooks, numLightSquareBishops;
   int numDarkSquareBishops, numKnights, numPawns;
   int numOppQueens, numOppRooks, numOppLightSquareBishops;
   int numOppDarkSquareBishops, numOppKnights, numOppPawns;
   int numPieces, numOppPieces;
   int numOppBishops, numOppMinors, numOppSliders;
   bool oppositeColoredBishops;

   getPieceTradeSignatures(&materialSignature, &oppMaterialSignature);

   getPieceCounters(materialSignature, &numQueens, &numRooks,
                    &numLightSquareBishops, &numDarkSquareBishops,
                    &numKnights, &numPawns);
   getPieceCounters(oppMaterialSignature, &numOppQueens, &numOppRooks,
                    &numOppLightSquareBishops, &numOppDarkSquareBishops,
                    &numOppKnights, &numOppPawns);
   numPieces = numQueens + numRooks + numLightSquareBishops +
      numDarkSquareBishops + numKnights;
   numOppBishops = numOppLightSquareBishops + numOppDarkSquareBishops;
   numOppMinors = numOppBishops + numOppKnights;
   numOppSliders = numOppQueens + numOppRooks + numOppBishops;
   numOppPieces = numOppSliders + numOppKnights;
   oppositeColoredBishops = (bool)
      (numLightSquareBishops + numDarkSquareBishops > 0 &&
       ((numOppLightSquareBishops > 0 && numLightSquareBishops == 0) ||
        (numOppDarkSquareBishops > 0 && numDarkSquareBishops == 0)));

   if (numPieces == 0)
   {
      return 0;
   }

   if (numPieces == 1)
   {
      if (numQueens > 0 && numOppRooks > 0 && numOppRooks + numOppMinors >= 2)
      {
         return 1;
      }

      if (numQueens > 0 && numOppKnights >= 2)
      {
         return 1;
      }

      if (numRooks > 0 && numOppQueens + numOppRooks > 0)
      {
         return 1;
      }

      if (numRooks > 0 && numOppMinors > 0)
      {
         return (numOppMinors == 1 ? 2 : 1);
      }

      if (numLightSquareBishops + numDarkSquareBishops + numKnights > 0)
      {
         return 0;
      }
   }
   else if (numPieces <= 3)
   {
      if (numQueens == 0 && numOppQueens > 0)
      {
         if (numRooks <= 1)
         {
            return 1;
         }
         else
         {
            return 8;           /* hard to win */
         }
      }

      if (numRooks + numQueens == 0 &&
          numLightSquareBishops + numDarkSquareBishops <= 1 &&
          numOppRooks + numOppQueens >= 1)
      {
         return (numOppQueens >= 1 ? 1 : 2);
      }
   }

   if (numLightSquareBishops == 1 && numDarkSquareBishops == 1)
   {
      if (numOppPieces == 1 && numOppRooks == 1)
      {
         return (numPieces == 2 ? 1 : 12);
      }

      if (numOppKnights > 0)
      {
         return 8;              /* hard to win sometimes */
      }
   }

   return 16;
}

static void initializeMaterialInfoTableCore1(const UINT32 signatureWhite,
                                             const UINT32 signatureBlack)
{
   const int index = signatureWhite + (signatureBlack << 9);
   SpecialEvalType specialEvalWhite = Se_None;
   SpecialEvalType specialEvalBlack = Se_None;
   const bool whiteMateMat =
      hasMaterialForMate(signatureWhite, signatureBlack, &specialEvalWhite,
                         FALSE);
   const bool blackMateMat =
      hasMaterialForMate(signatureBlack, signatureWhite, &specialEvalBlack,
                         FALSE);
   int numWhiteQueens, numWhiteRooks, numWhiteLightSquareBishops;
   int numWhiteDarkSquareBishops, numWhiteKnights, numWhitePawns;
   int numBlackQueens, numBlackRooks, numBlackLightSquareBishops;
   int numBlackDarkSquareBishops, numBlackKnights, numBlackPawns;
   int numWhitePieces, numBlackPieces;

   getPieceCounters(signatureWhite, &numWhiteQueens, &numWhiteRooks,
                    &numWhiteLightSquareBishops, &numWhiteDarkSquareBishops,
                    &numWhiteKnights, &numWhitePawns);
   getPieceCounters(signatureBlack, &numBlackQueens, &numBlackRooks,
                    &numBlackLightSquareBishops, &numBlackDarkSquareBishops,
                    &numBlackKnights, &numBlackPawns);

   numWhitePieces =
      numWhiteQueens + numWhiteRooks + numWhiteLightSquareBishops +
      numWhiteDarkSquareBishops + numWhiteKnights;
   numBlackPieces =
      numBlackQueens + numBlackRooks + numBlackLightSquareBishops +
      numBlackDarkSquareBishops + numBlackKnights;

   materialInfo[index].chancesWhite = (whiteMateMat == FALSE ? 0 : 16);
   materialInfo[index].chancesBlack = (blackMateMat == FALSE ? 0 : 16);
   materialInfo[index].specialEvalWhite = specialEvalWhite;
   materialInfo[index].specialEvalBlack = specialEvalBlack;

   if (whiteMateMat != FALSE)
   {
      if (numWhitePawns == 0)
      {
         if (hasMaterialForMate(signatureWhite, signatureBlack, 0, TRUE) ==
             FALSE)
         {
            materialInfo[index].chancesWhite = 1;
         }
         else
         {
            materialInfo[index].chancesWhite =
               getWinningChancesWithoutPawn(signatureWhite, signatureBlack);
         }
      }
      else
      {
         materialInfo[index].chancesWhite =
            getWinningChances(signatureWhite, signatureBlack);
      }
   }

   if (blackMateMat != FALSE)
   {
      if (numBlackPawns == 0)
      {
         if (hasMaterialForMate(signatureBlack, signatureWhite, 0, TRUE) ==
             FALSE)
         {
            materialInfo[index].chancesBlack = 1;
         }
         else
         {
            materialInfo[index].chancesBlack =
               getWinningChancesWithoutPawn(signatureBlack, signatureWhite);
         }
      }
      else
      {
         materialInfo[index].chancesBlack =
            getWinningChances(signatureBlack, signatureWhite);
      }
   }
}

void initializeMaterialInfoTable(const UINT32 signatureWhite,
                                 const UINT32 signatureBlack, int stage)
{
   int numQueens, numRooks, numLightSquareBishops, numDarkSquareBishops;
   int numKnights, numPawns;

   if (stage < 2)
   {
      for (numQueens = 0; numQueens <= 1; numQueens++)
      {
         for (numRooks = 0; numRooks <= 3; numRooks++)
         {
            for (numLightSquareBishops = 0; numLightSquareBishops <= 1;
                 numLightSquareBishops++)
            {
               for (numDarkSquareBishops = 0; numDarkSquareBishops <= 1;
                    numDarkSquareBishops++)
               {
                  for (numKnights = 0; numKnights <= 3; numKnights++)
                  {
                     for (numPawns = 0; numPawns <= 3; numPawns++)
                     {
                        UINT32 signature =
                           materialSignature(numQueens, numRooks,
                                             numLightSquareBishops,
                                             numDarkSquareBishops,
                                             numKnights,
                                             numPawns);

                        if (stage == 0)
                        {
                           initializeMaterialInfoTable(signature, 0, 1);
                        }
                        else
                        {
                           initializeMaterialInfoTable(signatureWhite,
                                                       signature, 2);
                        }
                     }
                  }
               }
            }
         }
      }
   }
   else
   {
      initializeMaterialInfoTableCore1(signatureWhite, signatureBlack);
   }
}

int initializeKingSafetyTable()
{
   const int MAX_MALUS = 500;
   const int MAX_STEP = 12;
   int i;

#ifdef TEST_SETUP
   const int factor = 12;
#else
   const int factor = 12;
#endif

   for (i = 0; i < KING_SAFETY_MALUS_DIM; i++)
   {
      KING_SAFETY_MALUS[i] = factor * (i * i) / 100;

      if (i > 0 && KING_SAFETY_MALUS[i] - KING_SAFETY_MALUS[i - 1] > MAX_STEP)
      {
         KING_SAFETY_MALUS[i] = KING_SAFETY_MALUS[i - 1] + MAX_STEP;
      }

      KING_SAFETY_MALUS[i] = min(KING_SAFETY_MALUS[i], MAX_MALUS);
   }

   return 0;
}

int opSw(const int value)
{
   return (value * 100) / 100;
}

int egSw(const int value)
{
   return (value * 100) / 100;
}

#ifdef HIGH_ATTACK_BONUS
int opAw(const int value)
{
   return (value * 100) / 256;
}

int egAw(const int value)
{
   return (value * 100) / 256;
}
#else
int opAw(const int value)
{
   return (value * 125) / 100;
}

int egAw(const int value)
{
   return (value * 125) / 100;
}
#endif

void initializeMobilityValues()
{
   int i;

   for (i = 0; i < MAX_MOVES_KNIGHT + 1; i++)
   {
      const INT32 value = KnightMobilityBonusSF[i];
      const int openingValue = (getOpeningValue(value) * 100) / 256;
      const int endgameValue = (getEndgameValue(value) * 100) / 256;
      const int scaledValue = V(openingValue, endgameValue);

      KnightMobilityBonus[i] = scaledValue;

      /* logDebug("Mk(%d)=(%d,%d)\n", i, getOpeningValue(scaledValue),
         getEndgameValue(scaledValue)); */
   }

   /* logDebug("\n"); */

   for (i = 0; i < MAX_MOVES_BISHOP + 1; i++)
   {
      const INT32 value = BishopMobilityBonusSF[i];
      const int openingValue = (getOpeningValue(value) * 100) / 256;
      const int endgameValue = (getEndgameValue(value) * 100) / 256;
      const int scaledValue = V(openingValue, endgameValue);

      BishopMobilityBonus[i] = scaledValue;

      /* logDebug("Mb(%d)=(%d,%d)\n", i, getOpeningValue(scaledValue),
         getEndgameValue(scaledValue)); */
   }

   /* logDebug("\n"); */

   for (i = 0; i < MAX_MOVES_ROOK + 1; i++)
   {
      const INT32 value = RookMobilityBonusSF[i];
      const int openingValue = (getOpeningValue(value) * 100) / 256;
      const int endgameValue = (getEndgameValue(value) * 100) / 256;
      const int scaledValue = V(openingValue, endgameValue);

      RookMobilityBonus[i] = scaledValue;

      /* logDebug("Mr(%d)=(%d,%d)\n", i, getOpeningValue(scaledValue),
         getEndgameValue(scaledValue)); */
   }

   /* logDebug("\n"); */

   for (i = 0; i < MAX_MOVES_QUEEN + 1; i++)
   {
      const INT32 value = QueenMobilityBonusSF[i];
      const int openingValue = (getOpeningValue(value) * 100) / 256;
      const int endgameValue = (getEndgameValue(value) * 100) / 256;
      const int scaledValue = V(openingValue, endgameValue);

      QueenMobilityBonus[i] = scaledValue;

      /* logDebug("Mq(%d)=(%d,%d)\n", i, getOpeningValue(scaledValue),
         getEndgameValue(scaledValue)); */
   }

   /* getKeyStroke(); */
}

int initializeModuleEvaluation()
{
   int i;
   Square square, kingsquare, catchersquare;

   centralFiles = squaresOfFile[FILE_D] | squaresOfFile[FILE_E];
   attackingRealm[WHITE] = squaresOfRank[RANK_5] | squaresOfRank[RANK_6] |
      squaresOfRank[RANK_7] | squaresOfRank[RANK_8];
   attackingRealm[BLACK] = squaresOfRank[RANK_4] | squaresOfRank[RANK_3] |
      squaresOfRank[RANK_2] | squaresOfRank[RANK_1];
   filesBCFG = squaresOfFileRange[FILE_B][FILE_C] |
      squaresOfFileRange[FILE_F][FILE_G];

   rookTraps[WHITE] = rookTraps[BLACK] = EMPTY_BITBOARD;
   setSquare(rookTraps[WHITE], A1);
   setSquare(rookTraps[WHITE], B1);
   setSquare(rookTraps[WHITE], C1);
   setSquare(rookTraps[WHITE], A2);
   setSquare(rookTraps[WHITE], B2);
   setSquare(rookTraps[WHITE], F1);
   setSquare(rookTraps[WHITE], G1);
   setSquare(rookTraps[WHITE], H1);
   setSquare(rookTraps[WHITE], G2);
   setSquare(rookTraps[WHITE], H2);

   ITERATE(square)
   {
      Color color;
      Square kingSquare;

      for (color = WHITE; color <= BLACK; color++)
      {
         passedPawnRectangle[color][square] =
            passedPawnCorridor[color][square] =
            candidateDefenders[color][square] =
            candidateSupporters[color][square] =
            pawnOpponents[color][square] = EMPTY_BITBOARD;
      }

      if (testSquare(rookTraps[WHITE], square))
      {
         setSquare(rookTraps[BLACK], getFlippedSquare(square));
      }

      ITERATE(kingSquare)
      {
         kingRealm[WHITE][square][kingSquare] =
            kingRealm[BLACK][square][kingSquare] = EMPTY_BITBOARD;
      }
   }

   ITERATE(square)
   {
      const int squarefile = file(square);
      const int squarerank = rank(square);
      int d1 = min(distance(square, D4), distance(square, E4));
      int d2 = min(distance(square, D5), distance(square, E5));
      int td1 = min(taxiDistance(square, D4), taxiDistance(square, E4));
      int td2 = min(taxiDistance(square, D5), taxiDistance(square, E5));

      centerDistance[square] = min(d1, d2);
      centerTaxiDistance[square] = min(td1, td2);
      butterflySquares[square] =
         generalMoves[KING][square] & ~squaresOfFile[squarefile];
      lateralSquares[square] =
         generalMoves[KING][square] & squaresOfRank[squarerank];
      companionFiles[square] =
         ((squaresOfFile[squarefile] & nonA) >> 1) |
         ((squaresOfFile[squarefile] & nonH) << 1);
      rookBlockers[square] = EMPTY_BITBOARD;

      ITERATE(kingsquare)
      {
         const int kingsquarefile = file(kingsquare);
         const int kingsquarerank = rank(kingsquare);
         Square targetSquare;

         if (kingsquarerank >= squarerank &&
             distance(square, kingsquare) <= 7 - squarerank)
         {
            setSquare(passedPawnRectangle[WHITE][square], kingsquare);
         }

         if (kingsquarerank <= squarerank &&
             distance(square, kingsquare) <= squarerank)
         {
            setSquare(passedPawnRectangle[BLACK][square], kingsquare);
         }

         if (kingsquarefile == squarefile)
         {
            if (kingsquarerank > squarerank)
            {
               setSquare(passedPawnCorridor[WHITE][square], kingsquare);
            }

            if (kingsquarerank < squarerank)
            {
               setSquare(passedPawnCorridor[BLACK][square], kingsquare);
            }
         }

         if (squarerank == kingsquarerank)
         {
            if (squarefile <= FILE_C && kingsquarefile <= FILE_C &&
                kingsquarefile > squarefile)
            {
               setSquare(rookBlockers[square], kingsquare);
            }

            if (squarefile >= FILE_F && kingsquarefile >= FILE_F &&
                kingsquarefile < squarefile)
            {
               setSquare(rookBlockers[square], kingsquare);
            }
         }

         ITERATE(targetSquare)
         {
            if (distance(square, targetSquare) <
                distance(kingsquare, targetSquare))
            {
               const Rank targetrank = rank(targetSquare);

               if (targetrank <= squarerank + 1)
               {
                  setSquare(kingRealm[WHITE][square][kingsquare],
                            targetSquare);
               }

               if (targetrank >= squarerank - 1)
               {
                  setSquare(kingRealm[BLACK][square][kingsquare],
                            targetSquare);
               }
            }
         }
      }

      ITERATE(catchersquare)
      {
         if (abs(file(catchersquare) - squarefile) == 1)
         {
            if (rank(catchersquare) > squarerank)
            {
               setSquare(candidateDefenders[WHITE][square], catchersquare);
            }

            if (rank(catchersquare) <= squarerank)
            {
               setSquare(candidateSupporters[WHITE][square], catchersquare);
            }

            if (rank(catchersquare) < squarerank)
            {
               setSquare(candidateDefenders[BLACK][square], catchersquare);
            }

            if (rank(catchersquare) >= squarerank)
            {
               setSquare(candidateSupporters[BLACK][square], catchersquare);
            }
         }

         if (abs(file(catchersquare) - squarefile) <= 1)
         {
            if (rank(catchersquare) >= squarerank)
            {
               setSquare(pawnOpponents[WHITE][square], catchersquare);
            }

            if (rank(catchersquare) <= squarerank)
            {
               setSquare(pawnOpponents[BLACK][square], catchersquare);
            }
         }
      }
   }

   ITERATE(square)
   {
      const int squarerank = rank(square);

      ITERATE(kingsquare)
      {
         if (squarerank >= RANK_2 && squarerank <= RANK_7)
         {
            passedPawnDefenderMalus[WHITE][WHITE][square][kingsquare] =
               getPassedPawnDefenderMalus(WHITE, WHITE, square, kingsquare);
            passedPawnDefenderMalus[WHITE][BLACK][square][kingsquare] =
               getPassedPawnDefenderMalus(WHITE, BLACK, square, kingsquare);
            passedPawnDefenderMalus[BLACK][WHITE][square][kingsquare] =
               getPassedPawnDefenderMalus(BLACK, WHITE, square, kingsquare);
            passedPawnDefenderMalus[BLACK][BLACK][square][kingsquare] =
               getPassedPawnDefenderMalus(BLACK, BLACK, square, kingsquare);
         }
         else
         {
            passedPawnDefenderMalus[WHITE][WHITE][square][kingsquare] =
               passedPawnDefenderMalus[WHITE][BLACK][square][kingsquare] =
               passedPawnDefenderMalus[BLACK][WHITE][square][kingsquare] =
               passedPawnDefenderMalus[BLACK][BLACK][square][kingsquare] = 0;
         }
      }
   }

   ITERATE(square)
   {
      const int dDark =
         min(taxiDistance(square, A1), taxiDistance(square, H8));
      const int dLight =
         min(taxiDistance(square, A8), taxiDistance(square, H1));
      const int dStandard = centerDistance[square];

      kingChaseMalus[DARK][square] = 3 * (7 - dDark) + dStandard;
      kingChaseMalus[LIGHT][square] = 3 * (7 - dLight) + dStandard;
      kingChaseMalus[ALL][square] = 6 - min(dDark, dLight) +
         centerDistance[square];
   }

   /*
      dumpBoardValues(kingChaseMalus[DARK]);
      dumpBoardValues(kingChaseMalus[LIGHT]);
      dumpBoardValues(kingChaseMalus[ALL]);
      getKeyStroke();
    */

   initializePieceSquareValues();
   initializeKingAttacks();
   initializeKingSafetyTable();

   attackPoints[WHITE_KING] = 0;
   attackPoints[WHITE_QUEEN] = QUEEN_BONUS_ATTACK;
   attackPoints[WHITE_ROOK] = ROOK_BONUS_ATTACK;
   attackPoints[WHITE_BISHOP] = BISHOP_BONUS_ATTACK;
   attackPoints[WHITE_KNIGHT] = KNIGHT_BONUS_ATTACK;
   attackPoints[WHITE_PAWN] = 0;
   attackPoints[BLACK_KING] = 0;
   attackPoints[BLACK_QUEEN] = QUEEN_BONUS_ATTACK;
   attackPoints[BLACK_ROOK] = ROOK_BONUS_ATTACK;
   attackPoints[BLACK_BISHOP] = BISHOP_BONUS_ATTACK;
   attackPoints[BLACK_KNIGHT] = KNIGHT_BONUS_ATTACK;
   attackPoints[BLACK_PAWN] = 0;

   weakOutpostSquareCandidates[WHITE] = weakOutpostSquareCandidates[BLACK] =
      EMPTY_BITBOARD;

   setSquare(weakOutpostSquareCandidates[WHITE], C4);
   setSquare(weakOutpostSquareCandidates[WHITE], D4);
   setSquare(weakOutpostSquareCandidates[WHITE], E4);
   setSquare(weakOutpostSquareCandidates[WHITE], F4);
   setSquare(weakOutpostSquareCandidates[WHITE], C3);
   setSquare(weakOutpostSquareCandidates[WHITE], D3);
   setSquare(weakOutpostSquareCandidates[WHITE], E3);
   setSquare(weakOutpostSquareCandidates[WHITE], F3);
   setSquare(weakOutpostSquareCandidates[BLACK], C5);
   setSquare(weakOutpostSquareCandidates[BLACK], D5);
   setSquare(weakOutpostSquareCandidates[BLACK], E5);
   setSquare(weakOutpostSquareCandidates[BLACK], F5);
   setSquare(weakOutpostSquareCandidates[BLACK], C6);
   setSquare(weakOutpostSquareCandidates[BLACK], D6);
   setSquare(weakOutpostSquareCandidates[BLACK], E6);
   setSquare(weakOutpostSquareCandidates[BLACK], F6);

   homeland[WHITE] = ( /* squaresOfRank[RANK_1] | */ squaresOfRank[RANK_2] |
                      squaresOfRank[RANK_3] | squaresOfRank[RANK_4]) &
      (squaresOfFile[FILE_C] | squaresOfFile[FILE_D] |
       squaresOfFile[FILE_E] | squaresOfFile[FILE_F]);
   /*
      homeland[WHITE] |= (squaresOfRank[RANK_1] |
      squaresOfRank[RANK_2] | squaresOfRank[RANK_3]) &
      (squaresOfFile[FILE_A] | squaresOfFile[FILE_B] |
      squaresOfFile[FILE_G] | squaresOfFile[FILE_H]);
    */
   homeland[BLACK] = getFlippedBitboard(homeland[WHITE]);

#ifdef GENERATE_TABLES
   generateKpkpTable();
#endif

   for (i = 0; i < 16; i++)
   {
      int j;

      for (j = 0; j < 16; j++)
      {
         sliderAttackPoints[i][j] = 0;
         piecePieceAttackBonus[i][j] = 0;
      }
   }

   sliderAttackPoints[WHITE_BISHOP][WHITE_PAWN] = V(opSw(3), egSw(5));
   sliderAttackPoints[WHITE_BISHOP][WHITE_KNIGHT] = V(opSw(3), egSw(5));
   sliderAttackPoints[WHITE_BISHOP][WHITE_ROOK] = V(opSw(3), egSw(5));
   sliderAttackPoints[WHITE_BISHOP][WHITE_KING] = V(opSw(3), egSw(5));
   sliderAttackPoints[WHITE_BISHOP][BLACK_PAWN] = V(opSw(2), egSw(5));
   sliderAttackPoints[WHITE_BISHOP][BLACK_KNIGHT] = V(opSw(2), egSw(5));
   sliderAttackPoints[WHITE_BISHOP][BLACK_ROOK] = V(opSw(15), egSw(25));
   sliderAttackPoints[WHITE_BISHOP][BLACK_QUEEN] = V(opSw(10), egSw(20));

   sliderAttackPoints[WHITE_ROOK][WHITE_KNIGHT] = V(opSw(3), egSw(5));
   sliderAttackPoints[WHITE_ROOK][WHITE_BISHOP] = V(opSw(3), egSw(5));
   sliderAttackPoints[WHITE_ROOK][WHITE_KING] = V(opSw(3), egSw(5));
   sliderAttackPoints[WHITE_ROOK][BLACK_PAWN] = V(opSw(2), egSw(5));
   sliderAttackPoints[WHITE_ROOK][BLACK_KNIGHT] = V(opSw(2), egSw(5));
   sliderAttackPoints[WHITE_ROOK][BLACK_BISHOP] = V(opSw(2), egSw(5));
   sliderAttackPoints[WHITE_ROOK][BLACK_QUEEN] = V(opSw(10), egSw(20));

   sliderAttackPoints[WHITE_QUEEN][WHITE_PAWN] = V(opSw(1), egSw(2));
   sliderAttackPoints[WHITE_QUEEN][WHITE_KNIGHT] = V(opSw(2), egSw(4));
   sliderAttackPoints[WHITE_QUEEN][WHITE_BISHOP] = V(opSw(2), egSw(4));
   sliderAttackPoints[WHITE_QUEEN][WHITE_ROOK] = V(opSw(2), egSw(4));
   sliderAttackPoints[WHITE_QUEEN][WHITE_KING] = V(opSw(2), egSw(4));
   sliderAttackPoints[WHITE_QUEEN][BLACK_KNIGHT] = V(opSw(2), egSw(5));
   sliderAttackPoints[WHITE_QUEEN][BLACK_BISHOP] = V(opSw(2), egSw(5));
   sliderAttackPoints[WHITE_QUEEN][BLACK_ROOK] = V(opSw(2), egSw(5));

#ifdef HIGH_ATTACK_BONUS
   piecePieceAttackBonus[WHITE_PAWN][BLACK_KNIGHT] = V(opAw(-56), egAw(-70));
   piecePieceAttackBonus[WHITE_PAWN][BLACK_BISHOP] = V(opAw(-56), egAw(-70));
   piecePieceAttackBonus[WHITE_PAWN][BLACK_ROOK] = V(opAw(-76), egAw(-99));
   piecePieceAttackBonus[WHITE_PAWN][BLACK_QUEEN] = V(opAw(-86), egAw(-118));

   piecePieceAttackBonus[WHITE_KNIGHT][BLACK_PAWN] = V(opAw(7), egAw(39));
   piecePieceAttackBonus[WHITE_KNIGHT][BLACK_BISHOP] = V(opAw(24), egAw(49));
   piecePieceAttackBonus[WHITE_KNIGHT][BLACK_ROOK] = V(opAw(41), egAw(100));
   piecePieceAttackBonus[WHITE_KNIGHT][BLACK_QUEEN] = V(opAw(41), egAw(100));

   piecePieceAttackBonus[WHITE_BISHOP][BLACK_PAWN] = V(opAw(7), egAw(39));
   piecePieceAttackBonus[WHITE_BISHOP][BLACK_KNIGHT] = V(opAw(24), egAw(49));
   piecePieceAttackBonus[WHITE_BISHOP][BLACK_ROOK] = V(opAw(41), egAw(100));
   piecePieceAttackBonus[WHITE_BISHOP][BLACK_QUEEN] = V(opAw(41), egAw(100));

   piecePieceAttackBonus[WHITE_ROOK][BLACK_PAWN] = V(opAw(5), egAw(29));
   piecePieceAttackBonus[WHITE_ROOK][BLACK_KNIGHT] = V(opAw(15), egAw(49));
   piecePieceAttackBonus[WHITE_ROOK][BLACK_BISHOP] = V(opAw(15), egAw(49));
   piecePieceAttackBonus[WHITE_ROOK][BLACK_QUEEN] = V(opAw(24), egAw(49));

   piecePieceAttackBonus[WHITE_QUEEN][BLACK_PAWN] = V(opAw(15), egAw(39));
   piecePieceAttackBonus[WHITE_QUEEN][BLACK_KNIGHT] = V(opAw(15), egAw(39));
   piecePieceAttackBonus[WHITE_QUEEN][BLACK_BISHOP] = V(opAw(15), egAw(39));
   piecePieceAttackBonus[WHITE_QUEEN][BLACK_ROOK] = V(opAw(15), egAw(39));
#else
   piecePieceAttackBonus[WHITE_PAWN][BLACK_KNIGHT] = V(opAw(-5), egAw(-7));
   piecePieceAttackBonus[WHITE_PAWN][BLACK_BISHOP] = V(opAw(-5), egAw(-7));
   piecePieceAttackBonus[WHITE_PAWN][BLACK_ROOK] = V(opAw(-8), egAw(-10));
   piecePieceAttackBonus[WHITE_PAWN][BLACK_QUEEN] = V(opAw(-8), egAw(-10));

   piecePieceAttackBonus[WHITE_KNIGHT][BLACK_PAWN] = V(opAw(3), egAw(4));
   piecePieceAttackBonus[WHITE_KNIGHT][BLACK_BISHOP] = V(opAw(4), egAw(5));
   piecePieceAttackBonus[WHITE_KNIGHT][BLACK_ROOK] = V(opAw(8), egAw(10));
   piecePieceAttackBonus[WHITE_KNIGHT][BLACK_QUEEN] = V(opAw(8), egAw(10));

   piecePieceAttackBonus[WHITE_BISHOP][BLACK_PAWN] = V(opAw(3), egAw(4));
   piecePieceAttackBonus[WHITE_BISHOP][BLACK_KNIGHT] = V(opAw(5), egAw(5));
   piecePieceAttackBonus[WHITE_BISHOP][BLACK_ROOK] = V(opAw(8), egAw(10));
   piecePieceAttackBonus[WHITE_BISHOP][BLACK_QUEEN] = V(opAw(8), egAw(10));

   piecePieceAttackBonus[WHITE_ROOK][BLACK_PAWN] = V(opAw(2), egAw(3));
   piecePieceAttackBonus[WHITE_ROOK][BLACK_KNIGHT] = V(opAw(4), egAw(5));
   piecePieceAttackBonus[WHITE_ROOK][BLACK_BISHOP] = V(opAw(4), egAw(5));
   piecePieceAttackBonus[WHITE_ROOK][BLACK_QUEEN] = V(opAw(8), egAw(10));

   piecePieceAttackBonus[WHITE_QUEEN][BLACK_PAWN] = V(opAw(1), egAw(1));
   piecePieceAttackBonus[WHITE_QUEEN][BLACK_KNIGHT] = V(opAw(2), egAw(2));
   piecePieceAttackBonus[WHITE_QUEEN][BLACK_BISHOP] = V(opAw(2), egAw(2));
   piecePieceAttackBonus[WHITE_QUEEN][BLACK_ROOK] = V(opAw(2), egAw(2));
#endif

   for (i = 0; i < 16; i++)
   {
      int j;

      for (j = 0; j < 16; j++)
      {
         if (pieceColor(i) == BLACK)
         {
            const Color reversedColor = opponent(pieceColor(j));
            PieceType attacker = (PieceType) (pieceType(i) | WHITE);
            PieceType attackedPiece =
               (PieceType) (pieceType(j) | reversedColor);

            sliderAttackPoints[i][j] =
               sliderAttackPoints[attacker][attackedPiece];
            piecePieceAttackBonus[i][j] =
               piecePieceAttackBonus[attacker][attackedPiece];
         }
      }
   }

   assert(sliderAttackPoints[BLACK_ROOK][WHITE_PAWN] ==
          sliderAttackPoints[WHITE_ROOK][BLACK_PAWN]);
   assert(sliderAttackPoints[BLACK_QUEEN][WHITE_BISHOP] ==
          sliderAttackPoints[WHITE_QUEEN][BLACK_BISHOP]);
   assert(sliderAttackPoints[BLACK_KNIGHT][WHITE_BISHOP] ==
          sliderAttackPoints[WHITE_KNIGHT][BLACK_BISHOP]);

   troitzkyArea[WHITE] =
      passedPawnCorridor[WHITE][A3] | passedPawnCorridor[WHITE][B5] |
      passedPawnCorridor[WHITE][C3] | passedPawnCorridor[WHITE][D3] |
      passedPawnCorridor[WHITE][E3] | passedPawnCorridor[WHITE][F3] |
      passedPawnCorridor[WHITE][G5] | passedPawnCorridor[WHITE][H3];
   troitzkyArea[BLACK] = getFlippedBitboard(troitzkyArea[WHITE]);

   krprkDrawFiles = squaresOfFile[FILE_A] | squaresOfFile[FILE_B] |
      squaresOfFile[FILE_G] | squaresOfFile[FILE_H];
   A1C1 = minValue[A1] | minValue[C1], F1H1 = minValue[F1] | minValue[H1];
   A1B1 = minValue[A1] | minValue[B1], G1H1 = minValue[G1] | minValue[H1];

   initializeMaterialInfoTable(0, 0, 0);

   transposeMatrix(MALUS_PAWN_SQUARE_TYPE_HR, MALUS_PAWN_SQUARE_TYPE);
   transposeMatrix(MALUS_KING_SQUARE_HR, MALUS_KING_SQUARE);

   initializeMobilityValues();

   return 0;
}

#ifndef NDEBUG
bool flipTest(Position * position,
              PawnHashInfo * pawnHashtable,
              KingSafetyHashInfo * kingsafetyHashtable)
{
   int v1, v2;
   EvaluationBase base;

   initializePosition(position);
   v1 = getValue(position, &base, pawnHashtable, kingsafetyHashtable);

   flipPosition(position);
   initializePosition(position);
   v2 = getValue(position, &base, pawnHashtable, kingsafetyHashtable);

   flipPosition(position);
   initializePosition(position);

   if (v1 != v2)
   {
      const int debugFlag = debugOutput;
      const bool debugEvalFlag = debugEval;

      debugOutput = TRUE;
      debugEval = TRUE;

      logDebug("flip test failed: v1=%d v2=%d\n", v1, v2);
      logPosition(position);
      logDebug("hash: %llu\n", position->hashValue);
      getValue(position, &base, pawnHashtable, kingsafetyHashtable);
      flipPosition(position);
      initializePosition(position);
      logPosition(position);
      getValue(position, &base, pawnHashtable, kingsafetyHashtable);
      flipPosition(position);
      initializePosition(position);

      debugEval = debugEvalFlag;
      debugOutput = debugFlag;
   }

   return (bool) (v1 == v2);
}
#endif

static int testPawnInfoGeneration()
{
   Variation variation;
   EvaluationBase base;

   initializeVariation(&variation,
                       "8/7p/5k2/5p2/p1p2P2/Pr1pPK2/1P1R3P/8 b - - 0 1");
   getPawnInfo(&variation.singlePosition, &base);

   assert(getNumberOfSetSquares(base.pawnProtectedSquares[WHITE]) == 8);
   assert(testSquare(base.pawnProtectedSquares[WHITE], B4));
   assert(testSquare(base.pawnProtectedSquares[WHITE], A3));
   assert(testSquare(base.pawnProtectedSquares[WHITE], C3));
   assert(testSquare(base.pawnProtectedSquares[WHITE], D4));
   assert(testSquare(base.pawnProtectedSquares[WHITE], F4));
   assert(testSquare(base.pawnProtectedSquares[WHITE], E5));
   assert(testSquare(base.pawnProtectedSquares[WHITE], G5));
   assert(testSquare(base.pawnProtectedSquares[WHITE], G3));

   assert(getNumberOfSetSquares(base.pawnProtectedSquares[BLACK]) == 7);
   assert(testSquare(base.pawnProtectedSquares[BLACK], B3));
   assert(testSquare(base.pawnProtectedSquares[BLACK], D3));
   assert(testSquare(base.pawnProtectedSquares[BLACK], C2));
   assert(testSquare(base.pawnProtectedSquares[BLACK], E2));
   assert(testSquare(base.pawnProtectedSquares[BLACK], E4));
   assert(testSquare(base.pawnProtectedSquares[BLACK], G4));
   assert(testSquare(base.pawnProtectedSquares[BLACK], G6));

   assert(getNumberOfSetSquares(base.passedPawns[WHITE]) == 0);
   assert(getNumberOfSetSquares(base.passedPawns[BLACK]) == 1);
   assert(testSquare(base.passedPawns[BLACK], D3));

   assert(getNumberOfSetSquares(base.weakPawns[WHITE]) == 3);
   assert(testSquare(base.weakPawns[WHITE], B2));
   assert(testSquare(base.weakPawns[WHITE], E3));
   assert(testSquare(base.weakPawns[WHITE], H2));

   assert(getNumberOfSetSquares(base.weakPawns[BLACK]) == 4);
   assert(testSquare(base.weakPawns[BLACK], A4));
   assert(testSquare(base.weakPawns[BLACK], C4));
   assert(testSquare(base.weakPawns[BLACK], F5));
   assert(testSquare(base.weakPawns[BLACK], H7));

   initializeVariation(&variation,
                       "4k3/2p5/p2p4/P2P4/1PP3p1/7p/7P/4K3 w - - 0 1");
   getPawnInfo(&variation.singlePosition, &base);

   assert(getNumberOfSetSquares(base.passedPawns[WHITE]) == 0);
   assert(getNumberOfSetSquares(base.passedPawns[BLACK]) == 0);
   assert(getNumberOfSetSquares(base.weakPawns[WHITE]) == 1);
   assert(testSquare(base.weakPawns[WHITE], H2));

   assert(getNumberOfSetSquares(base.weakPawns[BLACK]) == 3);
   assert(testSquare(base.weakPawns[BLACK], A6));
   assert(testSquare(base.weakPawns[BLACK], C7));
   assert(testSquare(base.weakPawns[BLACK], G4));

   return 0;
}

static int testWeakPawnDetection()
{
   Position position;
   Bitboard expectedResult = EMPTY_BITBOARD;
   EvaluationBase base;

   clearPosition(&position);
   position.piece[E1] = WHITE_KING;
   position.piece[E8] = BLACK_KING;
   position.piece[A3] = WHITE_PAWN;
   position.piece[B5] = WHITE_PAWN;
   position.piece[B6] = WHITE_PAWN;
   position.piece[C4] = WHITE_PAWN;
   position.piece[E4] = WHITE_PAWN;
   position.piece[G4] = WHITE_PAWN;
   position.piece[H2] = WHITE_PAWN;
   position.piece[A4] = BLACK_PAWN;
   position.piece[B7] = BLACK_PAWN;
   setSquare(expectedResult, A3);
   setSquare(expectedResult, E4);
   initializePosition(&position);
   getPawnInfo(&position, &base);
   assert(base.weakPawns[WHITE] == expectedResult);
   expectedResult = EMPTY_BITBOARD;
   setSquare(expectedResult, B7);
   assert(base.weakPawns[BLACK] == expectedResult);
   assert(base.candidatePawns[BLACK] == EMPTY_BITBOARD);

   position.piece[C4] = NO_PIECE;
   position.piece[C5] = WHITE_PAWN;
   initializePosition(&position);
   getPawnInfo(&position, &base);
   expectedResult = EMPTY_BITBOARD;
   setSquare(expectedResult, C5);
   assert(base.candidatePawns[WHITE] == expectedResult);

   clearPosition(&position);
   position.piece[E1] = WHITE_KING;
   position.piece[E8] = BLACK_KING;
   position.piece[A3] = WHITE_PAWN;
   position.piece[B5] = WHITE_PAWN;
   position.piece[B6] = WHITE_PAWN;
   position.piece[C4] = WHITE_PAWN;
   position.piece[E4] = WHITE_PAWN;
   position.piece[G4] = WHITE_PAWN;
   position.piece[H2] = WHITE_PAWN;
   position.piece[A4] = BLACK_PAWN;
   position.piece[B7] = BLACK_PAWN;
   position.piece[D6] = BLACK_PAWN;
   expectedResult = EMPTY_BITBOARD;
   setSquare(expectedResult, A3);
   setSquare(expectedResult, E4);
   setSquare(expectedResult, C4);
   initializePosition(&position);
   getPawnInfo(&position, &base);
   assert(base.weakPawns[WHITE] == expectedResult);
   assert(base.candidatePawns[WHITE] == EMPTY_BITBOARD);
   expectedResult = EMPTY_BITBOARD;
   setSquare(expectedResult, B7);
   setSquare(expectedResult, D6);
   assert(base.weakPawns[BLACK] == expectedResult);
   assert(base.candidatePawns[BLACK] == EMPTY_BITBOARD);

   position.piece[G5] = BLACK_PAWN;
   expectedResult = EMPTY_BITBOARD;
   setSquare(expectedResult, A3);
   setSquare(expectedResult, E4);
   setSquare(expectedResult, C4);
   setSquare(expectedResult, H2);
   initializePosition(&position);
   getPawnInfo(&position, &base);
   assert(base.weakPawns[WHITE] == expectedResult);
   assert(base.candidatePawns[WHITE] == EMPTY_BITBOARD);
   expectedResult = EMPTY_BITBOARD;
   setSquare(expectedResult, B7);
   setSquare(expectedResult, D6);
   setSquare(expectedResult, G5);
   assert(base.weakPawns[BLACK] == expectedResult);
   assert(base.candidatePawns[BLACK] == EMPTY_BITBOARD);

   return 0;
}

static int testBaseInitialization()
{
   Variation variation;

   initializeVariation(&variation, FEN_GAMESTART);

   assert(testSquare(passedPawnCorridor[WHITE][B4], B6));
   assert(testSquare(passedPawnCorridor[BLACK][B4], B6) == FALSE);
   assert(testSquare(passedPawnCorridor[WHITE][C2], H7) == FALSE);
   assert(testSquare(passedPawnCorridor[BLACK][G6], G2));

#ifndef NDEBUG
   {
      INT32 testBonus = evalBonus(-1, -1);

      assert(getOpeningValue(testBonus) == -1);
      assert(getEndgameValue(testBonus) == -1);
   }
#endif

   return 0;
}

/*
static int testFlippings()
{
   const char fen1[] =
      "2rr2k1/1b3ppp/pb2p3/1p2P3/1P2BPnq/P1N3P1/1B2Q2P/R4R1K b - - 0 1";
   const char fen2[] = "4k3/2p5/p2p4/P2P4/1PP3p1/7p/7P/4K3 w - - 0 1";
   const char fen3[] = "8/7p/5k2/5p2/p1p2P2/Pr1pPK2/1P1R3P/8 b - - 0 1";
   const char fen4[] =
      "6r1/Q2Pn2k/p1p1P2p/5p2/2PqR1r1/1P6/P6P/5R1K b - - 5 4";
   const char fen5[] =
      "Q4rk1/2bb1ppp/4pn2/pQ5q/3P4/N4N2/5PPP/R1B2RK1 w - a6 0 4";
   Variation variation;

   initializeVariation(&variation, fen1);
   assert(flipTest(&variation.singlePosition) != FALSE);

   initializeVariation(&variation, fen2);
   assert(flipTest(&variation.singlePosition) != FALSE);

   initializeVariation(&variation, fen3);
   assert(flipTest(&variation.singlePosition) != FALSE);

   initializeVariation(&variation, fen4);
   assert(flipTest(&variation.singlePosition) != FALSE);

   initializeVariation(&variation, fen5);
   assert(flipTest(&variation.singlePosition) != FALSE);

   return 0;
}
*/

int testModuleEvaluation()
{
   int result;

   if ((result = testPawnInfoGeneration()) != 0)
   {
      return result;
   }

   if ((result = testWeakPawnDetection()) != 0)
   {
      return result;
   }

   if ((result = testBaseInitialization()) != 0)
   {
      return result;
   }

   /*
      if ((result = testFlippings()) != 0)
      {
      return result;
      } 
    */

   return 0;
}
