/* $Id$ Copyright (C) 2006-2007 by David Cotton 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 2 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, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ package fr.free.jchecs.ai; import static fr.free.jchecs.ai.AbstractEngine.MATE_VALUE; import static fr.free.jchecs.core.Constants.FILE_COUNT; import static fr.free.jchecs.core.Constants.RANK_COUNT; import fr.free.jchecs.core.MoveGenerator; import fr.free.jchecs.core.Piece; import fr.free.jchecs.core.PieceType; import fr.free.jchecs.core.Square; /** * Fonction d'évaluation basée sur le matériel et la position des pièces présentes sur le plateau. * <p> * Classe sûre vis-à-vis des threads. * </p> * * @author David Cotton */ final class BoardControlHeuristic implements Heuristic { /** Identifiant de la classe pour la sérialisation. */ private static final long serialVersionUID = -7163145298434616262L; /** * Bonus/Malus d'un cavalier (blanc par défaut) en fonction de sa position. */ private static final int [] KNIGHT_POSITIONS = { // -50, -30, -30, -30, -30, -30, -30, -50, // a1 ... h1 -30, -20, -20, -20, -20, -20, -20, -30, // a2 ... h2 -20, 0, 20, 20, 20, 20, 0, -20, // a3 ... h3 -20, 0, 20, 20, 20, 20, 0, -20, // a4 ... h4 -20, 0, 10, 20, 20, 10, 0, -20, // a5 ... h5 -20, 0, 10, 10, 10, 10, 0, -20, // a6 ... h6 -20, -10, 0, 0, 0, 0, -10, -20, // a7 ... h7 -40, -20, -20, -20, -20, -20, -20, -40, // a8 ... h8 }; static { assert KNIGHT_POSITIONS.length == 64; } /** * Bonus/Malus d'un pion (blanc par défaut) en fonction de sa position. */ private static final int [] PAWN_POSITIONS = { 0, 0, 0, 0, 0, 0, 0, 0, // a1 ... h1 2, 2, 2, -2, -2, 2, 2, 2, // a2 ... h2 -2, -2, -2, 4, 4, -2, -2, -2, // a3 ... h3 0, 0, 0, 4, 4, 0, 0, 0, // a4 ... h4 2, 4, 6, 8, 8, 6, 4, 2, // a5 ... h5 4, 6, 8, 10, 10, 8, 6, 4, // a6 ... h6 4, 6, 8, 10, 10, 8, 6, 4, // a7 ... h7 500, 500, 500, 500, 500, 500, 500, 500, // a8 ... h8 }; static { assert PAWN_POSITIONS.length == 64; } /** * Bonus/Malus de base liés à la position d'une pièce (symétrique : adapté aux deux couleurs). */ private static final int [] DEFAULT_POSITIONS = { 0, 0, 0, 0, 0, 0, 0, 0, // a1 ... h1 0, 0, 0, 5, 5, 0, 0, 0, // a2 ... h2 0, 0, 5, 5, 5, 5, 0, 0, // a3 ... h3 0, 5, 5, 10, 10, 5, 5, 0, // a4 ... h4 0, 5, 5, 10, 10, 5, 5, 0, // a5 ... h5 0, 0, 5, 5, 5, 5, 0, 0, // a6 ... h6 0, 0, 0, 5, 5, 0, 0, 0, // a7 ... h7 0, 0, 0, 0, 0, 0, 0, 0, // a8 ... h8 }; static { assert DEFAULT_POSITIONS.length == 64; } /** * Crée une nouvelle instance. */ BoardControlHeuristic() { // Rien de spécifique... } /** * Renvoi la valeur estimée d'un état du jeu, pour les fonctions de recherche du meilleur coup. * * @param pEtat Etat du jeu. * @param pTrait Positionné à "true" si l'on veut une évaluation du point de vue des blancs. * @return Valeur estimée. * @see Heuristic#evaluate(MoveGenerator,boolean) */ public int evaluate(final MoveGenerator pEtat, final boolean pTrait) { assert pEtat != null; int res = -pEtat.getHalfmoveCount(); for (final Square s : Square.values()) { final Piece piece = pEtat.getPieceAt(s); if (piece != null) { final boolean traitPiece = piece.isWhite(); final PieceType typePiece = piece.getType(); final int val = typePiece.getValue(); final int pos; switch (typePiece) { case BISHOP : case QUEEN : case ROOK : pos = DEFAULT_POSITIONS[s.getIndex()]; break; case KING : if ((traitPiece != pTrait) && (pEtat.getFullmoveNumber() > 10) && pEtat.isInCheck(traitPiece)) { if (pEtat.getValidMoves(traitPiece).length == 0) { // Malus pour un mat... pos = MATE_VALUE; } else { // Malus pour un échec... pos = -250; } } else { // Pas de valeur de position pour le roi. pos = 0; } break; case KNIGHT : if (traitPiece) { pos = KNIGHT_POSITIONS[s.getIndex()]; } else { pos = KNIGHT_POSITIONS[((RANK_COUNT - 1) - s.getRank()) * FILE_COUNT + s.getFile()]; } break; case PAWN : if (traitPiece) { pos = PAWN_POSITIONS[s.getIndex()]; } else { pos = PAWN_POSITIONS[((RANK_COUNT - 1) - s.getRank()) * FILE_COUNT + s.getFile()]; } break; default : assert false; pos = 0; } final int score = val + pos; if (traitPiece == pTrait) { res += score; } else { res -= score; } } } return res; } }