/*
$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 java.util.Arrays;
import fr.free.jchecs.core.Board;
/**
* Classe implémentant la gestion d'une table de transposition intégrable dans un moteur d'I.A.
*
* @author David Cotton
*/
final class TranspositionTable
{
/** Nombre maximum de collisions entre les clés avant l'abandon. */
private static final int MAX_COLLISIONS = 3;
/** Identifiant d'une valeur de type "ALPHA". */
private static final int ALPHA = 0;
/** Identifiant d'une valeur de type "BETA". */
private static final int BETA = 1;
/** Identifiant d'une valeur de type "EXACT". */
private static final int EXACT = 2;
/** Tableau accueillant les clés de hachage identifiant les états de jeu. */
private final int [] _hashCodes;
/** Tableau types d'éléments. */
private final byte [] _types;
/** Tableau des profondeurs. */
private final byte [] _depths;
/** Tableau des valeurs. */
private final int [] _values;
/**
* Instancie une nouvelle table de transposition.
*
* @param pCapacite Taille maximale de la table de transposition.
*/
TranspositionTable(final int pCapacite)
{
assert pCapacite > 0;
_hashCodes = new int [ pCapacite ];
_types = new byte [ pCapacite ];
_depths = new byte [ pCapacite ];
_values = new int [ pCapacite ];
}
/**
* Vide la table de transposition.
*/
void clear()
{
Arrays.fill(_hashCodes, 0);
}
/**
* Renvoi l'éventuel valeur correspondant à un état de jeu dans le contexte de I.A. en cours.
*
* @param pEtat Etat recherché.
* @param pProfondeur Profondeur du résulat.
* @param pAlpha Valeur alpha.
* @param pBeta Valeur beta.
* @return Valeur correspondante, ou null si inconnue.
*/
Integer get(final Board pEtat, final int pProfondeur, final int pAlpha, final int pBeta)
{
assert pEtat != null;
// TODO: assert pAlpha <= pBeta;
final int cleCherchee = pEtat.hashCode();
final int capacite = _hashCodes.length;
int pos = Math.abs(cleCherchee % capacite);
int cle = _hashCodes[pos];
for (int i = MAX_COLLISIONS; (cle != 0) && (--i >= 0); /* Pré-décrémenté */)
{
if (cle == cleCherchee)
{
if (_depths[pos] >= pProfondeur)
{
final int type = _types[pos];
final int val = _values[pos];
if ((type == EXACT) || ((type == ALPHA) && (val <= pAlpha))
|| ((type == BETA) && (val >= pBeta)))
{
return Integer.valueOf(val);
}
}
break;
}
pos++;
if (pos >= capacite)
{
pos -= capacite;
}
cle = _hashCodes[pos];
}
return null;
}
/**
* Stocke la valeur donnée à un état dans la table de transposition.
*
* @param pEtat Etat de jeu.
* @param pProfondeur Profondeur liée au résultat.
* @param pAlpha Valeur alpha.
* @param pBeta Valeur beta.
* @param pValeur Valeur du résultat.
*/
void put(final Board pEtat, final int pProfondeur, final int pAlpha, final int pBeta,
final int pValeur)
{
assert pEtat != null;
// TODO: assert pAlpha <= pBeta;
final int cleEtat = pEtat.hashCode();
final int capacite = _hashCodes.length;
int pos = Math.abs(cleEtat % capacite);
int cle = _hashCodes[pos];
for (int i = MAX_COLLISIONS; (cle != 0) && (cle != cleEtat) && (--i >= 0); /* Pré-décrémenté */)
{
pos++;
if (pos >= capacite)
{
pos -= capacite;
}
cle = _hashCodes[pos];
}
if ((cle == 0) || ((cle == cleEtat) && (_depths[pos] < pProfondeur)))
{
byte type = EXACT;
if (pProfondeur > 0)
{
if (pValeur > pBeta)
{
type = BETA;
}
else if (pValeur < pAlpha)
{
type = ALPHA;
}
}
_hashCodes[pos] = cleEtat;
_types[pos] = type;
_depths[pos] = (byte) pProfondeur;
_values[pos] = pValeur;
}
}
}