#include <iomanip>
#include <sstream>
#include <math.h>
#include "TransTableL.h"
#include "debug.h"
extern unsigned char cardRank[16];
extern char relRank[8192][15];
static bool _constantsSet = false;
static int TTlowestRank[8192];
static unsigned maskBytes[8192][DDS_SUITS][TT_BYTES];
static vector<string> players =
{
"North", "East", "South", "West"
};
TransTableL::TransTableL()
{
if (! _constantsSet)
{
_constantsSet = true;
TransTableL::SetConstants();
}
poolp = nullptr;
pagesDefault = NUM_PAGES_DEFAULT;
pagesMaximum = NUM_PAGES_MAXIMUM;
pagesCurrent = 0;
memState = FROM_POOL;
harvestTrick = FIRST_HARVEST_TRICK;
harvestHand = 0;
harvested.nextBlockNo = 0;
timestamp = 0;
pageStats.numResets = 0;
pageStats.numCallocs = 0;
pageStats.numFrees = 0;
pageStats.numHarvests = 0;
pageStats.lastCurrent = 0;
TTInUse = 0;
}
TransTableL::~TransTableL()
{
TransTableL::ReturnAllMemory();
}
void TransTableL::SetConstants()
{
unsigned int topBitRank = 1;
TTlowestRank[0] = 15; unsigned winMask[8192];
winMask[0] = 0;
for (unsigned ind = 1; ind < 8192; ind++)
{
if (ind >= (topBitRank + topBitRank))
topBitRank <<= 1;
winMask[ind] = (winMask[ind ^ topBitRank] >> 2) | (3 << 24);
maskBytes[ind][0][0] = (winMask[ind] << 6) & 0xff000000;
maskBytes[ind][0][1] = (winMask[ind] << 14) & 0xff000000;
maskBytes[ind][0][2] = (winMask[ind] << 22) & 0xff000000;
maskBytes[ind][0][3] = (winMask[ind] << 30) & 0xff000000;
maskBytes[ind][1][0] = (winMask[ind] >> 2) & 0x00ff0000;
maskBytes[ind][1][1] = (winMask[ind] << 6) & 0x00ff0000;
maskBytes[ind][1][2] = (winMask[ind] << 14) & 0x00ff0000;
maskBytes[ind][1][3] = (winMask[ind] << 22) & 0x00ff0000;
maskBytes[ind][2][0] = (winMask[ind] >> 10) & 0x0000ff00;
maskBytes[ind][2][1] = (winMask[ind] >> 2) & 0x0000ff00;
maskBytes[ind][2][2] = (winMask[ind] << 6) & 0x0000ff00;
maskBytes[ind][2][3] = (winMask[ind] << 14) & 0x0000ff00;
maskBytes[ind][3][0] = (winMask[ind] >> 18) & 0x000000ff;
maskBytes[ind][3][1] = (winMask[ind] >> 10) & 0x000000ff;
maskBytes[ind][3][2] = (winMask[ind] >> 2) & 0x000000ff;
maskBytes[ind][3][3] = (winMask[ind] << 6) & 0x000000ff;
TTlowestRank[ind] = TTlowestRank[ind ^ topBitRank] - 1;
}
}
void TransTableL::Init(const int handLookup[][15])
{
unsigned int topBitRank = 1;
unsigned int topBitNo = 2;
aggrType * ap;
for (int s = 0; s < DDS_SUITS; s++)
{
aggr[0].aggrRanks[s] = 0;
aggr[0].aggrBytes[s][0] = 0;
aggr[0].aggrBytes[s][1] = 0;
aggr[0].aggrBytes[s][2] = 0;
aggr[0].aggrBytes[s][3] = 0;
}
for (unsigned ind = 1; ind < 8192; ind++)
{
if (ind >= (topBitRank << 1))
{
topBitRank <<= 1;
topBitNo++;
}
aggr[ind] = aggr[ind ^ topBitRank];
ap = &aggr[ind];
for (int s = 0; s < DDS_SUITS; s++)
{
ap->aggrRanks[s] = ap->aggrRanks[s] >> 2 |
static_cast<unsigned>(handLookup[s][topBitNo] << 24);
}
ap->aggrBytes[0][0] = (ap->aggrRanks[0] << 6) & 0xff000000;
ap->aggrBytes[0][1] = (ap->aggrRanks[0] << 14) & 0xff000000;
ap->aggrBytes[0][2] = (ap->aggrRanks[0] << 22) & 0xff000000;
ap->aggrBytes[0][3] = (ap->aggrRanks[0] << 30) & 0xff000000;
ap->aggrBytes[1][0] = (ap->aggrRanks[1] >> 2) & 0x00ff0000;
ap->aggrBytes[1][1] = (ap->aggrRanks[1] << 6) & 0x00ff0000;
ap->aggrBytes[1][2] = (ap->aggrRanks[1] << 14) & 0x00ff0000;
ap->aggrBytes[1][3] = (ap->aggrRanks[1] << 22) & 0x00ff0000;
ap->aggrBytes[2][0] = (ap->aggrRanks[2] >> 10) & 0x0000ff00;
ap->aggrBytes[2][1] = (ap->aggrRanks[2] >> 2) & 0x0000ff00;
ap->aggrBytes[2][2] = (ap->aggrRanks[2] << 6) & 0x0000ff00;
ap->aggrBytes[2][3] = (ap->aggrRanks[2] << 14) & 0x0000ff00;
ap->aggrBytes[3][0] = (ap->aggrRanks[3] >> 18) & 0x000000ff;
ap->aggrBytes[3][1] = (ap->aggrRanks[3] >> 10) & 0x000000ff;
ap->aggrBytes[3][2] = (ap->aggrRanks[3] >> 2) & 0x000000ff;
ap->aggrBytes[3][3] = (ap->aggrRanks[3] << 6) & 0x000000ff;
}
}
void TransTableL::SetMemoryDefault(int megabytes)
{
double blockMem = BLOCKS_PER_PAGE * sizeof(winBlockType) /
static_cast<double>(1024.);
pagesDefault = static_cast<int>((1024 * megabytes) / blockMem);
}
void TransTableL::SetMemoryMaximum(int megabytes)
{
double blockMem = BLOCKS_PER_PAGE * sizeof(winBlockType) /
static_cast<double>(1024.);
pagesMaximum = static_cast<int>((1024 * megabytes) / blockMem);
}
void TransTableL::MakeTT()
{
if (! TTInUse)
{
TTInUse = 1;
for (int t = 0; t < TT_TRICKS; t++)
{
for (int h = 0; h < DDS_HANDS; h++)
{
TTroot[t][h] = static_cast<distHashType *>
(malloc(256 * sizeof(distHashType)));
if (TTroot[t][h] == nullptr)
exit(1);
}
}
}
TransTableL::InitTT();
}
void TransTableL::InitTT()
{
for (int c = 0; c < TT_TRICKS; c++)
{
for (int h = 0; h < DDS_HANDS; h++)
{
for (int i = 0; i < 256; i++)
{
TTroot[c][h][i].nextNo = 0;
TTroot[c][h][i].nextWriteNo = 0;
}
lastBlockSeen[c][h] = nullptr;
}
}
}
void TransTableL::ReleaseTT()
{
if (! TTInUse)
return;
TTInUse = 0;
for (int t = 0; t < TT_TRICKS; t++)
{
for (int h = 0; h < DDS_HANDS; h++)
{
if (TTroot[t][h] == nullptr)
continue;
free(TTroot[t][h]);
}
}
}
void TransTableL::ResetMemory(const TTresetReason reason)
{
UNUSED(reason);
if (poolp == nullptr)
return;
pageStats.numResets++;
pageStats.numCallocs += pagesCurrent - pageStats.lastCurrent;
pageStats.lastCurrent = pagesCurrent;
while (pagesCurrent > pagesDefault)
{
free(poolp->list);
poolp = poolp->prev;
free(poolp->next);
poolp->next = nullptr;
pagesCurrent--;
}
pageStats.numFrees += pageStats.lastCurrent - pagesCurrent;
pageStats.lastCurrent = pagesCurrent;
while (poolp->prev)
poolp = poolp->prev;
poolp->nextBlockNo = 0;
nextBlockp = poolp->list;
TransTableL::InitTT();
timestamp = 0;
memState = FROM_POOL;
return;
}
void TransTableL::ReturnAllMemory()
{
poolType * tmp;
if (poolp)
{
while (poolp->next)
poolp = poolp->next;
while (poolp)
{
free(poolp->list);
tmp = poolp;
poolp = poolp->prev;
free(tmp);
}
}
pagesCurrent = 0;
pageStats.numResets = 0;
pageStats.numCallocs = 0;
pageStats.numFrees = 0;
pageStats.numHarvests = 0;
pageStats.lastCurrent = 0;
TransTableL::ReleaseTT();
return;
}
int TransTableL::BlocksInUse() const
{
poolType * pp = poolp;
int count = 0;
do
{
count += pp->nextBlockNo;
pp = pp->prev;
}
while (pp);
return count;
}
double TransTableL::MemoryInUse() const
{
int blockMem = BLOCKS_PER_PAGE * pagesCurrent *
static_cast<int>(sizeof(winBlockType));
int aggrMem = 8192 * static_cast<int>(sizeof(aggrType));
int rootMem = TT_TRICKS * DDS_HANDS * 256 *
static_cast<int>(sizeof(distHashType));
return (blockMem + aggrMem + rootMem) / static_cast<double>(1024.);
}
TransTableL::winBlockType * TransTableL::GetNextCardBlock()
{
if (poolp == nullptr)
{
poolp = static_cast<poolType *>(calloc(1, sizeof(poolType)));
if (poolp == nullptr)
exit(1);
poolp->list = static_cast<winBlockType *>
(malloc(BLOCKS_PER_PAGE * sizeof(winBlockType)));
if (! poolp->list)
exit(1);
poolp->next = nullptr;
poolp->prev = nullptr;
poolp->nextBlockNo = 1;
nextBlockp = poolp->list;
pagesCurrent++;
return nextBlockp++;
}
else if (memState == FROM_HARVEST)
{
int n = harvested.nextBlockNo;
if (n == BLOCKS_PER_PAGE)
{
if (! TransTableL::Harvest())
{
TransTableL::ResetMemory(TT_RESET_UNKNOWN);
poolp->nextBlockNo++;
return nextBlockp++;
}
n = 0;
}
harvested.nextBlockNo++;
return harvested.list[n];
}
else if (poolp->nextBlockNo == BLOCKS_PER_PAGE)
{
if (poolp->next)
{
poolp = poolp->next;
poolp->nextBlockNo = 1;
nextBlockp = poolp->list;
return nextBlockp++;
}
else if (pagesCurrent == pagesMaximum)
{
if (! TransTableL::Harvest())
{
TransTableL::ResetMemory(TT_RESET_UNKNOWN);
poolp->nextBlockNo++;
return nextBlockp++;
}
memState = FROM_HARVEST;
harvested.nextBlockNo++;
return harvested.list[0];
}
else
{
poolType * newpoolp = static_cast<poolType *>
(calloc(1, sizeof(poolType)));
if (newpoolp == nullptr)
{
if (! TransTableL::Harvest())
{
TransTableL::ResetMemory(TT_RESET_UNKNOWN);
poolp->nextBlockNo++;
return nextBlockp++;
}
memState = FROM_HARVEST;
harvested.nextBlockNo++;
return harvested.list[0];
}
newpoolp->list = static_cast<winBlockType *>
(malloc(BLOCKS_PER_PAGE * sizeof(winBlockType)));
if (! newpoolp->list)
{
if (! TransTableL::Harvest())
{
TransTableL::ResetMemory(TT_RESET_UNKNOWN);
poolp->nextBlockNo++;
return nextBlockp++;
}
memState = FROM_HARVEST;
harvested.nextBlockNo++;
return harvested.list[0];
}
newpoolp->nextBlockNo = 1;
newpoolp->next = nullptr;
newpoolp->prev = poolp;
poolp->next = newpoolp;
poolp = newpoolp;
nextBlockp = newpoolp->list;
pagesCurrent++;
return nextBlockp++;
}
}
poolp->nextBlockNo++;
return nextBlockp++;
}
bool TransTableL::Harvest()
{
distHashType * rootptr = TTroot[harvestTrick][harvestHand];
distHashType * ptr;
winBlockType * bp;
int trick = harvestTrick;
int hand = harvestHand;
int hash, suit, hno = 0;
while (1)
{
for (hash = 0; hash < 256; hash++)
{
ptr = &rootptr[hash];
for (suit = ptr->nextNo - 1; suit >= 0; suit--)
{
bp = ptr->list[suit].posBlock;
if (timestamp - bp->timestampRead > HARVEST_AGE)
{
bp->nextMatchNo = 0;
bp->nextWriteNo = 0;
bp->timestampRead = timestamp;
harvested.list[hno] = bp;
if (suit != ptr->nextNo - 1)
ptr->list[suit] = ptr->list[ ptr->nextNo - 1 ];
ptr->nextNo--;
ptr->nextWriteNo = ptr->nextNo;
if (++hno == BLOCKS_PER_PAGE)
{
if (++harvestHand >= DDS_HANDS)
{
harvestHand = 0;
if (--harvestTrick < 0)
harvestTrick = FIRST_HARVEST_TRICK;
}
harvested.nextBlockNo = 0;
pageStats.numHarvests++;
return true;
}
}
}
}
if (++harvestHand >= DDS_HANDS)
{
harvestHand = 0;
if (--harvestTrick < 0)
harvestTrick = FIRST_HARVEST_TRICK;
}
if (harvestTrick == trick && harvestHand == hand)
return false;
rootptr = TTroot[harvestTrick][harvestHand];
}
}
int TransTableL::hash8(const int handDist[]) const
{
int h =
(handDist[0] ^
((handDist[1] * 5) ) ^
((handDist[2] * 25) ) ^
((handDist[3] * 125) ) );
return (h ^ (h >> 5)) & 0xff;
}
nodeCardsType * TransTableL::Lookup(
const int tricks,
const int hand,
const unsigned short aggrTarget[],
const int handDist[],
const int limit,
bool& lowerFlag)
{
long long suitLengths =
(static_cast<long long>(handDist[0]) << 36) |
(static_cast<long long>(handDist[1]) << 24) |
(static_cast<long long>(handDist[2]) << 12) |
(static_cast<long long>(handDist[3]) );
int hashkey = hash8(handDist);
bool empty;
lastBlockSeen[tricks][hand] =
LookupSuit(&TTroot[tricks][hand][hashkey], suitLengths, empty);
if (empty)
return nullptr;
unsigned * ab0 = aggr[ aggrTarget[0] ].aggrBytes[0];
unsigned * ab1 = aggr[ aggrTarget[1] ].aggrBytes[1];
unsigned * ab2 = aggr[ aggrTarget[2] ].aggrBytes[2];
unsigned * ab3 = aggr[ aggrTarget[3] ].aggrBytes[3];
winMatchType TTentry;
TTentry.topSet1 = ab0[0] | ab1[0] | ab2[0] | ab3[0];
TTentry.topSet2 = ab0[1] | ab1[1] | ab2[1] | ab3[1];
TTentry.topSet3 = ab0[2] | ab1[2] | ab2[2] | ab3[2];
TTentry.topSet4 = ab0[3] | ab1[3] | ab2[3] | ab3[3];
return TransTableL::LookupCards(TTentry,
lastBlockSeen[tricks][hand], limit, lowerFlag);
}
TransTableL::winBlockType * TransTableL::LookupSuit(
distHashType * dp,
const long long key,
bool& empty)
{
int n = dp->nextNo;
for (int i = 0; i < n; i++)
{
if (dp->list[i].key == key)
{
empty = false;
return dp->list[i].posBlock;
}
}
empty = true;
int m;
if (n == DISTS_PER_ENTRY)
{
if (dp->nextWriteNo == DISTS_PER_ENTRY)
{
m = 0;
dp->nextWriteNo = 1;
}
else
m = dp->nextWriteNo++;
}
else
{
winBlockType * bp = GetNextCardBlock();
m = dp->nextWriteNo++;
dp->list[m].posBlock = bp;
dp->list[m].posBlock->timestampRead = timestamp;
dp->nextNo++;
}
dp->list[m].key = key;
dp->list[m].posBlock->nextMatchNo = 0;
dp->list[m].posBlock->nextWriteNo = 0;
return dp->list[m].posBlock;
}
nodeCardsType * TransTableL::LookupCards(
const winMatchType& search,
winBlockType * bp,
const int limit,
bool& lowerFlag)
{
const int n = bp->nextWriteNo - 1;
winMatchType * wp = &bp->list[n];
for (int i = n; i >= 0; i--, wp--)
{
if ((wp->topSet1 ^ search.topSet1) & wp->topMask1)
continue;
if (wp->lastMaskNo != 1)
{
if ((wp->topSet2 ^ search.topSet2) & wp->topMask2)
continue;
if (wp->lastMaskNo != 2)
{
if ((wp->topSet3 ^ search.topSet3) & wp->topMask3)
continue;
}
}
nodeCardsType * nodep = &wp->first;
if (nodep->lbound > limit)
{
bp->timestampRead = ++timestamp;
lowerFlag = true;
return nodep;
}
else if (nodep->ubound <= limit)
{
bp->timestampRead = ++timestamp;
lowerFlag = false;
return nodep;
}
}
const int n2 = bp->nextMatchNo - 1;
wp = &bp->list[n2];
for (int i = n2; i > n; i--, wp--)
{
if ((wp->topSet1 ^ search.topSet1) & wp->topMask1)
continue;
if (wp->lastMaskNo != 1)
{
if ((wp->topSet2 ^ search.topSet2) & wp->topMask2)
continue;
if (wp->lastMaskNo != 2)
{
if ((wp->topSet3 ^ search.topSet3) & wp->topMask3)
continue;
}
}
nodeCardsType * nodep = &wp->first;
if (nodep->lbound > limit)
{
lowerFlag = true;
bp->timestampRead = ++timestamp;
return nodep;
}
else if (nodep->ubound <= limit)
{
lowerFlag = false;
bp->timestampRead = ++timestamp;
return nodep;
}
}
return nullptr;
}
void TransTableL::CreateOrUpdate(
winBlockType * bp,
const winMatchType& search,
const bool flag)
{
winMatchType * wp = bp->list;
int n = bp->nextMatchNo;
for (int i = 0; i < n; i++, wp++)
{
if (wp->xorSet != search.xorSet ) continue;
if (wp->maskIndex != search.maskIndex) continue;
if (wp->topSet1 != search.topSet1 ) continue;
if (wp->topSet2 != search.topSet2 ) continue;
if (wp->topSet3 != search.topSet3 ) continue;
nodeCardsType& node = wp->first;
if (search.first.lbound > node.lbound)
node.lbound = search.first.lbound;
if (search.first.ubound < node.ubound)
node.ubound = search.first.ubound;
node.bestMoveSuit = search.first.bestMoveSuit;
node.bestMoveRank = search.first.bestMoveRank;
return;
}
if (n == BLOCKS_PER_ENTRY)
{
if (bp->nextWriteNo >= BLOCKS_PER_ENTRY)
bp->nextWriteNo = 0;
}
else
bp->nextMatchNo++;
wp = &bp->list[ bp->nextWriteNo++ ];
*wp = search;
if (!flag)
{
wp->first.bestMoveSuit = 0;
wp->first.bestMoveRank = 0;
}
}
void TransTableL::Add(
const int tricks,
const int hand,
const unsigned short aggrTarget[],
const unsigned short ourWinRanks[],
const nodeCardsType& first,
const bool flag)
{
if (lastBlockSeen[tricks][hand] == nullptr)
{
return;
}
unsigned * ab[DDS_SUITS];
unsigned * mb[DDS_SUITS];
char low[DDS_SUITS];
unsigned short int ag;
int w;
winMatchType TTentry;
TTentry.first = first;
TTentry.xorSet = 0;
for (int ss = 0; ss < DDS_SUITS; ss++)
{
w = static_cast<int>(ourWinRanks[ss]);
if (w == 0)
{
ab[ss] = aggr[0].aggrBytes[ss];
mb[ss] = maskBytes[0][ss];
low[ss] = 15;
TTentry.first.leastWin[ss] = 0;
}
else
{
w = w & (-w);
ag = static_cast<unsigned short>(aggrTarget[ss] & (-w));
ab[ss] = aggr[ag].aggrBytes[ss];
mb[ss] = maskBytes[ag][ss];
low[ss] = static_cast<char>(TTlowestRank[ag]);
TTentry.first.leastWin[ss] = 15 - low[ss];
TTentry.xorSet ^= aggr[ag].aggrRanks[ss];
}
}
TTentry.topSet1 = ab[0][0] | ab[1][0] | ab[2][0] | ab[3][0];
TTentry.topSet2 = ab[0][1] | ab[1][1] | ab[2][1] | ab[3][1];
TTentry.topSet3 = ab[0][2] | ab[1][2] | ab[2][2] | ab[3][2];
TTentry.topSet4 = ab[0][3] | ab[1][3] | ab[2][3] | ab[3][3];
TTentry.topMask1 = mb[0][0] | mb[1][0] | mb[2][0] | mb[3][0];
TTentry.topMask2 = mb[0][1] | mb[1][1] | mb[2][1] | mb[3][1];
TTentry.topMask3 = mb[0][2] | mb[1][2] | mb[2][2] | mb[3][2];
TTentry.topMask4 = mb[0][3] | mb[1][3] | mb[2][3] | mb[3][3];
TTentry.maskIndex =
(low[0] << 12) | (low[1] << 8) | (low[2] << 4) | low[3];
if (TTentry.topMask2 == 0)
TTentry.lastMaskNo = 1;
else if (TTentry.topMask3 == 0)
TTentry.lastMaskNo = 2;
else if (TTentry.topMask4 == 0)
TTentry.lastMaskNo = 3;
else
TTentry.lastMaskNo = 4;
TransTableL::CreateOrUpdate(lastBlockSeen[tricks][hand],
TTentry, flag);
}
void TransTableL::PrintMatch(
ofstream& fout,
const winMatchType& wp,
const unsigned char lengths[DDS_HANDS][DDS_SUITS]) const
{
vector<vector<string>> hands;
hands.resize(DDS_HANDS);
for (unsigned i = 0; i < DDS_HANDS; i++)
hands[i].resize(DDS_SUITS);
TransTableL::SetToPartialHands(wp.topSet1, wp.topMask1, 14, 4, hands);
TransTableL::SetToPartialHands(wp.topSet2, wp.topMask2, 10, 4, hands);
TransTableL::SetToPartialHands(wp.topSet3, wp.topMask3, 6, 4, hands);
TransTableL::SetToPartialHands(wp.topSet4, wp.topMask4, 2, 1, hands);
TransTableL::DumpHands(fout, hands, lengths);
TransTableL::PrintNodeValues(fout, wp.first);
}
void TransTableL::PrintNodeValues(
ofstream& fout,
const nodeCardsType& np) const
{
fout << setw(16) << left << "Lowest used" <<
cardSuit[0] << cardRank[15-static_cast<int>(np.leastWin[0])] << ", " <<
cardSuit[1] << cardRank[15-static_cast<int>(np.leastWin[1])] << ", " <<
cardSuit[2] << cardRank[15-static_cast<int>(np.leastWin[2])] << ", " <<
cardSuit[3] << cardRank[15-static_cast<int>(np.leastWin[3])] << "\n";
fout << setw(16) << left << "Bounds" <<
to_string(static_cast<int>(np.lbound)) << " to " <<
to_string(static_cast<int>(np.ubound)) << " tricks\n";
fout << setw(16) << left << "Best move" <<
cardSuit[ static_cast<int>(np.bestMoveSuit) ] <<
cardRank[ static_cast<int>(np.bestMoveRank) ] << "\n\n";
}
string TransTableL::MakeHolding(
const string& high,
const unsigned len) const
{
const size_t l = high.size();
if (l == 0)
return "-";
else if (l == len)
return high;
else
return high.substr(0, l) + string(len-l, 'x');
}
void TransTableL::DumpHands(
ofstream& fout,
const vector<vector<string>>& hands,
const unsigned char lengths[][DDS_SUITS]) const
{
for (unsigned i = 0; i < DDS_SUITS; i++)
{
fout << setw(16) << "" <<
TransTableL::MakeHolding(hands[0][i], lengths[0][i]) << "\n";
}
for (unsigned i = 0; i < DDS_SUITS; i++)
{
fout << setw(16) << left <<
TransTableL::MakeHolding(hands[3][i], lengths[3][i]) <<
setw(16) << "" <<
setw(16) <<
TransTableL::MakeHolding(hands[1][i], lengths[1][i]) << "\n";
}
for (unsigned i = 0; i < DDS_SUITS; i++)
{
fout << setw(16) << "" <<
TransTableL::MakeHolding(hands[2][i], lengths[2][i]) << "\n";
}
fout << "\n";
}
void TransTableL::SetToPartialHands(
const unsigned set,
const unsigned mask,
const int maxRank,
const int numRanks,
vector<vector<string>>& hands) const
{
for (unsigned s = 0; s < DDS_SUITS; s++)
{
for (int rank = maxRank; rank > maxRank - numRanks; rank--)
{
int shift = 8 * static_cast<int>(3 - s) + 2 * (rank - maxRank + 3);
unsigned maskCard = mask >> shift;
if (maskCard & 3)
{
unsigned player = (set >> shift) & 3;
hands[player][s] += static_cast<char>(cardRank[rank]);
}
}
}
}
void TransTableL::KeyToDist(
const long long key,
int handDist[]) const
{
handDist[0] = static_cast<int>((key >> 36) & 0x00000fff);
handDist[1] = static_cast<int>((key >> 24) & 0x00000fff);
handDist[2] = static_cast<int>((key >> 12) & 0x00000fff);
handDist[3] = static_cast<int>((key ) & 0x00000fff);
}
void TransTableL::DistToLengths(
const int trick,
const int handDist[],
unsigned char lengths[DDS_HANDS][DDS_SUITS]) const
{
for (int h = 0; h < DDS_HANDS; h++)
{
lengths[h][0] = static_cast<unsigned char>((handDist[h] >> 8) & 0xf);
lengths[h][1] = static_cast<unsigned char>((handDist[h] >> 4) & 0xf);
lengths[h][2] = static_cast<unsigned char>((handDist[h] ) & 0xf);
lengths[h][3] = static_cast<unsigned char>
(trick + 1 - lengths[h][0] - lengths[h][1] - lengths[h][2]);
}
}
string TransTableL::SingleLenToStr(const unsigned char len[]) const
{
return to_string(static_cast<unsigned>(len[0])) + "=" +
to_string(static_cast<unsigned>(len[1])) + "=" +
to_string(static_cast<unsigned>(len[2])) + "=" +
to_string(static_cast<unsigned>(len[3]));
}
string TransTableL::LenToStr(
const unsigned char len[DDS_HANDS][DDS_SUITS]) const
{
return TransTableL::SingleLenToStr(len[0]) + " " +
TransTableL::SingleLenToStr(len[1]) + " " +
TransTableL::SingleLenToStr(len[2]) + " " +
TransTableL::SingleLenToStr(len[3]);
}
void TransTableL::PrintSuits(
ofstream& fout,
const int trick,
const int hand) const
{
distHashType * dp;
int handDist[DDS_HANDS];
unsigned char len[DDS_HANDS][DDS_SUITS];
fout << setw(4) << left << "Key" <<
setw(3) << right << "No" <<
setw(8) << right << players[0] <<
setw(8) << players[1] <<
setw(8) << players[2] <<
setw(8) << players[3] << "\n";
for (int hashkey = 0; hashkey < 256; hashkey++)
{
dp = &TTroot[trick][hand][hashkey];
if (dp->nextNo == 0)
continue;
for (int i = 0; i < dp->nextNo; i++)
{
if (i == 0)
fout << "0x" << setw(2) << hex << hashkey <<
setw(3) << right << dec << dp->nextNo << " ";
else
fout << setw(8) << "";
TransTableL::KeyToDist(dp->list[i].key, handDist);
TransTableL::DistToLengths(trick, handDist, len);
fout << TransTableL::LenToStr(len) << "\n";
}
}
fout << "\n";
}
void TransTableL::PrintAllSuits(ofstream& fout) const
{
for (int trick = 11; trick >= 1; trick--)
{
for (int hand = 0; hand < DDS_HANDS; hand++)
{
fout << "Trick " << trick << ", hand " <<
players[static_cast<unsigned>(hand)] << "\n";
fout << string(20, '=') << "\n\n";
TransTableL::PrintSuits(fout, trick, hand);
}
}
}
void TransTableL::MakeHistStats(
const int hist[],
int& count,
int& prod_sum,
int& prod_sumsq,
int& max_len,
const int last_index) const
{
count = 0;
prod_sum = 0;
prod_sumsq = 0;
max_len = 0;
for (int i = 1; i <= last_index; i++)
{
if (hist[i])
{
prod_sum += i * hist[i];
prod_sumsq += i * i * hist[i];
count += hist[i];
if (i > max_len)
max_len = i;
}
}
}
int TransTableL::CalcPercentile(
const int hist[],
const double threshold,
const int last_index) const
{
int cum = 0;
for (int i = 1; i <= last_index; i++)
{
cum += hist[i];
if (cum >= threshold)
return i;
}
return -1;
}
void TransTableL::PrintHist(
ofstream& fout,
const int hist[],
const int num_wraps,
const int last_index) const
{
int count, prod_sum, prod_sumsq, max_len;
TransTableL::MakeHistStats(hist,
count, prod_sum, prod_sumsq, max_len, last_index);
for (int i = 1; i <= last_index; i++)
if (hist[i])
fout << setw(7) << right << i <<
setw(6) << right << hist[i] << "\n";
fout << "\n";
fout << setw(7) << left << "Entries" <<
setw(6) << right << count << "\n";
if (count > 1)
{
fout << setw(7) << left << "Full" <<
setw(6) << right << num_wraps << "\n";
double mean = prod_sum / static_cast<double>(count);
fout << setw(7) << left << "Average" <<
setw(6) << right << setprecision(2) << fixed << mean << "\n";
double var = (prod_sumsq - count*mean*mean) /
static_cast<double>(count-1);
if (var >= 0.)
fout << setw(7) << left << "Std.dev" <<
setw(6) << right << setprecision(2) << fixed << sqrt(var) << "\n";
fout << setw(7) << left << "Maximum" <<
setw(6) << right << max_len << "\n";
}
fout << "\n";
}
void TransTableL::UpdateSuitHist(
const int trick,
const int hand,
int hist[],
int& num_wraps) const
{
distHashType * dp;
num_wraps = 0;
for (int i = 0; i <= DISTS_PER_ENTRY; i++)
hist[i] = 0;
for (int hashkey = 0; hashkey < 256; hashkey++)
{
dp = &TTroot[trick][hand][hashkey];
hist [ dp->nextNo ]++;
if (dp->nextNo != dp->nextWriteNo)
num_wraps++; }
}
void TransTableL::UpdateSuitHist(
const int trick,
const int hand,
int hist[],
int suitHist[],
int& num_wraps,
int& suitWraps) const
{
distHashType * dp;
num_wraps = 0;
for (int i = 0; i <= DISTS_PER_ENTRY; i++)
hist[i] = 0;
for (int hashkey = 0; hashkey < 256; hashkey++)
{
dp = &TTroot[trick][hand][hashkey];
hist [ dp->nextNo ]++;
suitHist[ dp->nextNo ]++;
if (dp->nextNo != dp->nextWriteNo)
{
num_wraps++; suitWraps++;
}
}
}
void TransTableL::PrintSuitStats(
ofstream& fout,
const int trick,
const int hand) const
{
int hist[DISTS_PER_ENTRY+1];
int num_wraps;
TransTableL::UpdateSuitHist(trick, hand, hist, num_wraps);
fout << "Suit histogram for trick " << trick << ", hand " <<
players[static_cast<unsigned>(hand)] << "\n";
TransTableL::PrintHist(fout, hist, num_wraps, DISTS_PER_ENTRY);
}
void TransTableL::PrintAllSuitStats(ofstream& fout) const
{
int num_wraps;
int suitWraps = 0;
int hist[DISTS_PER_ENTRY+1];
int suitHist[DISTS_PER_ENTRY+1];
for (int i = 0; i <= DISTS_PER_ENTRY; i++)
suitHist[i] = 0;
for (int trick = 11; trick >= 1; trick--)
{
for (int hand = 0; hand < DDS_HANDS; hand++)
{
TransTableL::UpdateSuitHist(trick, hand, hist, suitHist,
num_wraps, suitWraps);
fout << "Suit histogram for trick " << trick << ", hand " <<
players[static_cast<unsigned>(hand)] << "\n";
TransTableL::PrintHist(fout, hist, num_wraps, DISTS_PER_ENTRY);
}
}
fout << "Overall suit histogram\n";
TransTableL::PrintHist(fout, suitHist, suitWraps, DISTS_PER_ENTRY);
}
void TransTableL::PrintSummarySuitStats(ofstream& fout) const
{
int hist[DISTS_PER_ENTRY+1];
int count, prod_sum, prod_sumsq, max_len, num_wraps;
fout << "Suit depth statistics\n\n";
fout << setw(5) << right << "Trick" <<
setw(7) << "Player" <<
setw(8) << "Entries" <<
setw(8) << "Full" <<
setw(8) << "Average" <<
setw(8) << "Std.dev" <<
setw(8) << "Maximum" <<
" P" << setw(4) << setprecision(2) << fixed << TT_PERCENTILE << "\n";
for (int trick = 11; trick >= 1; trick--)
{
for (int hand = 0; hand < DDS_HANDS; hand++)
{
TransTableL::UpdateSuitHist(trick, hand, hist, num_wraps);
TransTableL::MakeHistStats(hist,
count, prod_sum, prod_sumsq, max_len, DISTS_PER_ENTRY);
double mean = 0., var = 0.;
if (count > 1)
{
mean = prod_sum / static_cast<double>(count);
var = (prod_sumsq - count*mean*mean) /
static_cast<double>(count-1);
if (var < 0.)
var = 0.;
}
const int percentile =
TransTableL::CalcPercentile(hist,
TT_PERCENTILE * count, DISTS_PER_ENTRY);
fout << setw(5) << right << trick <<
setw(7) << players[static_cast<unsigned>(hand)] <<
setw(8) << count <<
setw(8) << num_wraps;
if (count > 0)
fout << setw(8) << mean <<
setw(8) << setprecision(2) << fixed << sqrt(var);
else
fout << setw(8) << '-' << setw(8) << '-';
fout << setw(8) << max_len <<
setw(8) << setprecision(2) << fixed << percentile << "\n";
}
fout << "\n";
}
fout << "\n";
}
TransTableL::winBlockType const * TransTableL::FindMatchingDist(
const int trick,
const int hand,
const int handDistSought[]) const
{
winBlockType * bp;
distHashType * dp;
int handDist[DDS_HANDS];
for (int hashkey = 0; hashkey < 256; hashkey++)
{
dp = &TTroot[trick][hand][hashkey];
for (int i = 0; i < dp->nextNo; i++)
{
bp = dp->list[i].posBlock;
TransTableL::KeyToDist(dp->list[i].key, handDist);
bool same = true;
for (int h = 0; h < DDS_HANDS; h++)
{
if (handDist[h] != handDistSought[h])
{
same = false;
break;
}
}
if (same)
return bp;
}
}
return nullptr;
}
void TransTableL::PrintEntriesBlock(
ofstream& fout,
winBlockType const * bp,
const unsigned char lengths[DDS_HANDS][DDS_SUITS]) const
{
string st = to_string(bp->nextMatchNo) +
" matches for " + TransTableL::LenToStr(lengths);
fout << st << "\n" << string(st.size(), '=') << "\n\n";
for (int j = 0; j < bp->nextMatchNo; j++)
{
st = "Entry number " + to_string(j+1);
fout << st << "\n";
fout << string(st.size(), '-') << "\n\n";
TransTableL::PrintMatch(fout, bp->list[j], lengths);
}
}
void TransTableL::PrintEntriesDistAndCards(
ofstream& fout,
const int trick,
const int hand,
const unsigned short aggrTarget[],
const int handDist[]) const
{
unsigned char len[DDS_HANDS][DDS_SUITS];
winBlockType const * bp =
TransTableL::FindMatchingDist(trick, hand, handDist);
TransTableL::DistToLengths(trick, handDist, len);
fout << "Looking up entry for trick " << trick << ", hand " <<
players[static_cast<unsigned>(hand)] << "\n";
fout << TransTableL::LenToStr(len) << "\n\n";
if (! bp)
{
fout << "Entry not found\n\n";
return;
}
unsigned const * ab0 = aggr[ aggrTarget[0] ].aggrBytes[0];
unsigned const * ab1 = aggr[ aggrTarget[1] ].aggrBytes[1];
unsigned const * ab2 = aggr[ aggrTarget[2] ].aggrBytes[2];
unsigned const * ab3 = aggr[ aggrTarget[3] ].aggrBytes[3];
winMatchType TTentry;
TTentry.topSet1 = ab0[0] | ab1[0] | ab2[0] | ab3[0];
TTentry.topSet2 = ab0[1] | ab1[1] | ab2[1] | ab3[1];
TTentry.topSet3 = ab0[2] | ab1[2] | ab2[2] | ab3[2];
TTentry.topSet4 = ab0[3] | ab1[3] | ab2[3] | ab3[3];
int matchNo = 1;
int n = bp->nextMatchNo - 1;
winMatchType const * wp = &bp->list[n];
for (int i = n; i >= 0; i--, wp--)
{
if ((wp->topSet1 ^ TTentry.topSet1) & wp->topMask1)
continue;
if (wp->lastMaskNo != 1)
{
if ((wp->topSet2 ^ TTentry.topSet2) & wp->topMask2)
continue;
if (wp->lastMaskNo != 2)
{
if ((wp->topSet3 ^ TTentry.topSet3) & wp->topMask3)
continue;
}
}
fout << "Match number " << matchNo++ << "\n";
fout << string(15, '-') << "\n";
TransTableL::PrintMatch(fout, bp->list[i], len);
}
if (matchNo == 1)
fout << n << " matches for suit, none for cards\n\n";
else
fout << "\n";
}
void TransTableL::PrintEntriesDist(
ofstream& fout,
const int trick,
const int hand,
const int handDist[]) const
{
unsigned char len[DDS_HANDS][DDS_SUITS];
winBlockType const * bp =
TransTableL::FindMatchingDist(trick, hand, handDist);
TransTableL::DistToLengths(trick, handDist, len);
if (! bp)
{
fout << "Entry not found: Trick " << trick << ", hand " <<
players[static_cast<unsigned>(hand)] << "\n";
fout << TransTableL::LenToStr(len) << "\n\n";
return;
}
TransTableL::PrintEntriesBlock(fout, bp, len);
}
void TransTableL::PrintEntries(
ofstream& fout,
const int trick,
const int hand) const
{
winBlockType * bp;
distHashType * dp;
int handDist[DDS_HANDS];
unsigned char lengths[DDS_HANDS][DDS_SUITS];
for (int hashkey = 0; hashkey < 256; hashkey++)
{
dp = &TTroot[trick][hand][hashkey];
for (int i = 0; i < dp->nextNo; i++)
{
bp = dp->list[i].posBlock;
TransTableL::KeyToDist(dp->list[i].key, handDist);
TransTableL::DistToLengths(trick, handDist, lengths);
TransTableL::PrintEntriesBlock(fout, bp, lengths);
}
}
}
void TransTableL::PrintAllEntries(ofstream& fout) const
{
for (int trick = 11; trick >= 1; trick--)
{
for (int hand = 0; hand < DDS_HANDS; hand++)
{
const string st = "Entries, trick " + to_string(trick) +
", hand " + players[static_cast<unsigned>(hand)];
fout << st << "\n";
fout << string(st.size(), '=') << "\n\n";
TransTableL::PrintEntries(fout, trick, hand);
}
}
fout << "\n";
}
void TransTableL::UpdateEntryHist(
const int trick,
const int hand,
int hist[],
int& num_wraps) const
{
distHashType * dp;
num_wraps = 0;
for (int i = 0; i <= BLOCKS_PER_ENTRY; i++)
hist[i] = 0;
for (int hashkey = 0; hashkey < 256; hashkey++)
{
dp = &TTroot[trick][hand][hashkey];
for (int i = 0; i < dp->nextNo; i++)
{
int c = dp->list[i].posBlock->nextMatchNo;
hist [c]++;
if (c != dp->list[i].posBlock->nextWriteNo)
num_wraps++; }
}
}
void TransTableL::UpdateEntryHist(
const int trick,
const int hand,
int hist[],
int suitHist[],
int& num_wraps,
int& suitWraps) const
{
distHashType * dp;
num_wraps = 0;
for (int i = 0; i <= BLOCKS_PER_ENTRY; i++)
hist[i] = 0;
for (int hashkey = 0; hashkey < 256; hashkey++)
{
dp = &TTroot[trick][hand][hashkey];
for (int i = 0; i < dp->nextNo; i++)
{
int c = dp->list[i].posBlock->nextMatchNo;
hist [c]++;
suitHist[c]++;
if (c != dp->list[i].posBlock->nextWriteNo)
{
num_wraps++; suitWraps++;
}
}
}
}
void TransTableL::PrintEntryStats(
ofstream& fout,
const int trick,
const int hand) const
{
int hist[BLOCKS_PER_ENTRY+1];
int num_wraps;
TransTableL::UpdateEntryHist(trick, hand, hist, num_wraps);
fout << "Entry histogram for trick " << trick << ", hands " <<
players[static_cast<unsigned>(hand)] << "\n";
TransTableL::PrintHist(fout, hist, num_wraps, BLOCKS_PER_ENTRY);
}
void TransTableL::PrintAllEntryStats(ofstream& fout) const
{
int hist[BLOCKS_PER_ENTRY+1];
int num_wraps;
int suitWraps = 0;
int suitHist[BLOCKS_PER_ENTRY+1];
for (int i = 0; i <= BLOCKS_PER_ENTRY; i++)
suitHist[i] = 0;
for (int trick = 11; trick >= 1; trick--)
{
for (int hand = 0; hand < DDS_HANDS; hand++)
{
TransTableL::UpdateEntryHist(trick, hand, hist, suitHist,
num_wraps, suitWraps);
fout << "Entry histogram for trick " << trick << ", hands " <<
players[static_cast<unsigned>(hand)] << "\n";
TransTableL::PrintHist(fout, hist, num_wraps, BLOCKS_PER_ENTRY);
}
}
fout << "Overall entry histogram\n";
TransTableL::PrintHist(fout, suitHist, suitWraps, BLOCKS_PER_ENTRY);
}
int TransTableL::EffectOfBlockBound(
const int hist[],
const int size) const
{
int cum_memory = 0;
int unit_size = 0;
for (int i = 1; i <= BLOCKS_PER_ENTRY; i++)
{
if ((i - 1) % size == 0)
unit_size += size;
cum_memory += hist[i] * unit_size;
}
return cum_memory;
}
void TransTableL::PrintSummaryEntryStats(ofstream& fout) const
{
int hist[BLOCKS_PER_ENTRY + 1];
int count, prod_sum, prod_sumsq, max_len, num_wraps;
int cumCount = 0;
double cumProd = 0.;
int cumMemory = 0;
fout << "Entry depth statistics\n\n";
fout << setw(5) << right << "Trick" <<
setw(7) << "Player" <<
setw(8) << "Entries" <<
setw(8) << "Full" <<
setw(8) << "Average" <<
setw(8) << "Std.dev" <<
setw(8) << "Maximum" <<
" P" << setw(4) << setprecision(2) << fixed << TT_PERCENTILE << "\n";
for (int trick = 11; trick >= 1; trick--)
{
for (int hand = 0; hand < DDS_HANDS; hand++)
{
TransTableL::UpdateEntryHist(trick, hand, hist, num_wraps);
TransTableL::MakeHistStats(hist,
count, prod_sum, prod_sumsq, max_len, BLOCKS_PER_ENTRY);
cumCount += count;
cumProd += prod_sum;
cumMemory += TransTableL::EffectOfBlockBound(hist, 20);
double mean = prod_sum / static_cast<double>(count);
double var = (count > 1 ?
(prod_sumsq - count*mean*mean) /
static_cast<double>(count-1) : 0.);
if (var < 0.)
var = 0.;
const int percentile = TransTableL::CalcPercentile(hist,
TT_PERCENTILE * count, BLOCKS_PER_ENTRY);
fout << setw(5) << right << trick <<
setw(7) << players[static_cast<unsigned>(hand)] <<
setw(8) << count <<
setw(8) << num_wraps <<
setw(8) << mean <<
setw(8) << sqrt(var) <<
setw(8) << max_len <<
setw(8) << setprecision(2) << fixed << percentile << "\n";
}
fout << "\n";
}
fout << "\n";
fout << setw(16) << left << "Blocks counted " <<
setw(8) << right << cumCount << "\n";
fout << setw(16) << left << "Blocks produced " <<
setw(8) << right << TransTableL::BlocksInUse() << "\n";
fout << setw(16) << left << "Mem scenario" <<
setw(7) << right << setprecision(2) << fixed <<
100. * cumMemory /
(static_cast<double>(BLOCKS_PER_ENTRY * cumCount)) << "%\n";
if (cumCount)
fout << setw(16) << left << "Fullness" <<
setw(7) << right << setprecision(2) << fixed <<
100. * cumProd / (BLOCKS_PER_ENTRY * cumCount) << "%\n";
fout << "\n";
}