/*
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 <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <math.h>
#include "hash.h"
#include "protector.h"
#include "io.h"
#include "keytable.h"
#define NODE_TABLE_SIZE (MAX_THREADS * MAX_DEPTH * 32)
const unsigned int NUM_DATES = 16;
const unsigned int CLUSTER_SIZE = 4;
const UINT8 DEPTH_NONE = 0;
Nodeentry nodeUsageTable[NODE_TABLE_SIZE];
long numNodeTableEntries;
INT16 getHashentryValue(const Hashentry * entry)
{
return (INT16) (entry->data & 0xFFFF);
}
INT16 getHashentryStaticValue(const Hashentry * entry)
{
return (INT16) ((entry->data >> 16) & 0xFFFF);
}
UINT8 getHashentryImportance(const Hashentry * entry)
{
return (UINT8) ((entry->data >> 32) & 0xFF);
}
UINT16 getHashentryMove(const Hashentry * entry)
{
return (UINT16) ((entry->data >> 40) & 0xFFFF);
}
UINT8 getHashentryDate(const Hashentry * entry)
{
return (UINT8) ((entry->data >> 56) & 0x0F);
}
UINT8 getHashentryFlag(const Hashentry * entry)
{
return (UINT8) ((entry->data >> 60) & 0x03);
}
UINT64 getHashentryKey(const Hashentry * entry)
{
return entry->key ^ entry->data;
}
static int getAge(const Hashtable * hashtable, const UINT8 date)
{
assert(hashtable
->date
< NUM_DATES
);
return (hashtable->date + NUM_DATES - date) & (NUM_DATES - 1);
}
void incrementDate(Hashtable * hashtable)
{
assert(hashtable
->date
< NUM_DATES
);
hashtable->date = (UINT8) ((hashtable->date + 1) % NUM_DATES);
assert(hashtable
->date
< NUM_DATES
);
}
static void deleteTables(Hashtable * hashtable)
{
if (hashtable->table != 0)
{
hashtable->table = 0;
}
}
static UINT64 _getHashData(INT16 value, INT16 staticValue, UINT8 importance,
UINT16 bestMove, UINT8 date, UINT8 flag)
{
return ((UINT64) (value & 0xFFFF)) |
((UINT64) (staticValue & 0xFFFF)) << 16 |
((UINT64) importance) << 32 |
((UINT64) bestMove) << 40 |
((UINT64) date) << 56 | ((UINT64) flag) << 60;
}
Hashentry constructHashEntry(UINT64 key, INT16 value, INT16 staticValue,
UINT8 importance, UINT16 bestMove, UINT8 date,
UINT8 flag)
{
Hashentry entry;
entry.key = key;
entry.data = _getHashData(value, staticValue, importance,
bestMove, date, flag);
return entry;
}
void resetHashtable(Hashtable * hashtable)
{
UINT64 l;
Hashentry emptyEntry;
emptyEntry.key = ULONG_ZERO;
emptyEntry.data = _getHashData(-VALUE_MATED, 0, DEPTH_NONE,
(UINT16) NO_MOVE, 0, HASHVALUE_UPPER_LIMIT);
for (l = 0; l < hashtable->tableSize + CLUSTER_SIZE; l++)
{
hashtable->table[l] = emptyEntry;
}
hashtable->date = 0;
hashtable->entriesUsed = 0;
/* logDebug("hashtable reset done.\n"); */
}
void resetNodetable()
{
int i;
for (i = 0; i < NODE_TABLE_SIZE; i++)
{
nodeUsageTable[i].key = ULONG_ZERO;
}
}
void initializeHashtable(Hashtable * hashtable)
{
hashtable->table = 0;
hashtable->tableSize = 0;
hashtable->entriesUsed = 0;
}
bool isPrimeNumber(UINT64 n)
{
UINT64 limit, d;
if (n == 2)
{
return TRUE;
}
if (n < 2 || n % 2 == 0)
{
return FALSE;
}
limit
= (UINT64
) (sqrt((double) n
) + 1.0);
for (d = 3; d <= limit; d += 2)
{
if (n % d == 0)
{
return FALSE;
}
}
return TRUE;
}
UINT64 getNextPrime(UINT64 n)
{
while (isPrimeNumber(n) == FALSE)
{
n++;
}
return n;
}
UINT64 getPreviousPrime(UINT64 n)
{
n--;
while (isPrimeNumber(n) == FALSE)
{
n--;
}
return n;
}
void setHashtableSize(Hashtable * hashtable, UINT64 size)
{
const UINT64 ENTRY_SIZE = sizeof(Hashentry);
deleteTables(hashtable);
hashtable->tableSize = getNextPrime(size / ENTRY_SIZE);
hashtable->table =
malloc((size_t)((hashtable
->tableSize
+ CLUSTER_SIZE
) * ENTRY_SIZE
)); // Pierre-Marie Baty -- added type cast
/* logDebug("Hashtable size: %ld entries\n",
hashtable->tableSize); */
}
UINT64 getHashIndex(Hashtable * hashtable, UINT64 key)
{
return key % ((hashtable)->tableSize);
}
void setHashentry(Hashtable * hashtable, UINT64 key, INT16 value,
UINT8 importance, UINT16 bestMove, UINT8 flag,
INT16 staticValue)
{
const UINT64 index = getHashIndex(hashtable, key);
UINT64 data, i, bestEntry = 0;
int bestEntryScore = -1024;
Hashentry *entryToBeReplaced;
for (i = 0; i < CLUSTER_SIZE; i++)
{
Hashentry copy = hashtable->table[index + i];
const UINT8 copyDate = getHashentryDate(©);
const int copyFlag = getHashentryFlag(©);
if (getHashentryKey(©) == key || copy.key == ULONG_ZERO)
{
if (copyDate != hashtable->date || copy.key == ULONG_ZERO)
{
hashtable->entriesUsed++;
}
if (bestMove == (UINT16) NO_MOVE)
{
bestMove = getHashentryMove(©);
}
data = _getHashData(value, staticValue, importance, bestMove,
hashtable->date, flag);
hashtable->table[index + i].key = key ^ data;
hashtable->table[index + i].data = data;
return;
}
else
{
const int score =
getAge(hashtable, copyDate) * 8 -
getHashentryImportance(©) -
(copyFlag == HASHVALUE_EXACT ? 24 : 0);
if (score > bestEntryScore)
{
bestEntry = i;
bestEntryScore = score;
}
}
}
if (getHashentryDate(&hashtable->table[index + bestEntry]) !=
hashtable->date)
{
hashtable->entriesUsed++;
}
entryToBeReplaced = &hashtable->table[index + bestEntry];
data = _getHashData(value, staticValue, importance, bestMove,
hashtable->date, flag);
entryToBeReplaced->key = key ^ data;
entryToBeReplaced->data = data;
}
Hashentry *getHashentry(Hashtable * hashtable, UINT64 key)
{
const UINT64 index = getHashIndex(hashtable, key);
UINT64 i;
for (i = 0; i < CLUSTER_SIZE; i++)
{
Hashentry *tableEntry = &hashtable->table[index + i];
if (getHashentryKey(tableEntry) == key)
{
if (getHashentryDate(tableEntry) != hashtable->date)
{
#ifndef NDEBUG
const Hashentry originalEntry = *tableEntry;
#endif
const UINT64 mask = (((UINT64) 0x0F) << 56);
const UINT64 newData = (tableEntry->data & ~mask) |
(((UINT64) hashtable->date) << 56);
tableEntry->key = key ^ newData;
tableEntry->data = newData;
hashtable->entriesUsed++;
assert(getHashentryValue
(&originalEntry
) ==
getHashentryValue(tableEntry));
assert(getHashentryStaticValue
(&originalEntry
) ==
getHashentryStaticValue(tableEntry));
assert(getHashentryImportance
(&originalEntry
) ==
getHashentryImportance(tableEntry));
assert(getHashentryMove
(&originalEntry
) ==
getHashentryMove(tableEntry));
assert(getHashentryFlag
(&originalEntry
) ==
getHashentryFlag(tableEntry));
assert(hashtable
->date
== getHashentryDate
(tableEntry
));
}
return tableEntry;
}
}
return 0;
}
UINT64 getNodeIndex(UINT64 key)
{
return key % numNodeTableEntries;
}
bool nodeIsInUse(UINT64 key, UINT8 depth)
{
UINT64 nodeIndex = getNodeIndex(key);
return nodeUsageTable[nodeIndex].key == key &&
nodeUsageTable[nodeIndex].depth >= depth && key != ULONG_ZERO;
}
bool setNodeUsage(UINT64 key, UINT8 depth)
{
UINT64 nodeIndex = getNodeIndex(key);
if (nodeUsageTable[nodeIndex].key == ULONG_ZERO)
{
nodeUsageTable[nodeIndex].key = key;
nodeUsageTable[nodeIndex].depth = depth;
return TRUE;
}
else
{
return FALSE;
}
}
void resetNodeUsage(UINT64 key, UINT8 depth)
{
UINT64 nodeIndex = getNodeIndex(key);
nodeUsageTable[nodeIndex].key = ULONG_ZERO;
}
int initializeModuleHash()
{
resetNodetable();
numNodeTableEntries = (long)getPreviousPrime(NODE_TABLE_SIZE); // Pierre-Marie Baty -- added type cast
return 0;
}
static int testAgeCalculation()
{
Hashtable hashtable;
hashtable.date = 0;
assert(getAge
(&hashtable
, (UINT8
) (NUM_DATES
- 1)) == 1);
assert(getAge
(&hashtable
, 0) == 0);
assert(getAge
(&hashtable
, 1) == (int) (NUM_DATES
- 1));
hashtable.date = 2;
assert(getAge
(&hashtable
, 1) == 1);
assert(getAge
(&hashtable
, 2) == 0);
assert(getAge
(&hashtable
, 3) == (int) (NUM_DATES
- 1));
hashtable.date = (UINT8) (NUM_DATES - 1);
assert(getAge
(&hashtable
, (UINT8
) (NUM_DATES
- 2)) == 1);
assert(getAge
(&hashtable
, (UINT8
) (NUM_DATES
- 1)) == 0);
assert(getAge
(&hashtable
, 0) == (int) (NUM_DATES
- 1));
return (hashtable.date == (UINT8) (NUM_DATES - 1) ? 0 : 1);
}
int testModuleHash()
{
int result = 0;
if ((result = testAgeCalculation()) != 0)
{
return result;
}
return 0;
}