perfect: Import Muehle and weaselLibrary

See:
https://github.com/calcitem/Muehle
https://github.com/calcitem/weaselLibrary
This commit is contained in:
Calcitem 2021-01-18 00:31:03 +08:00
parent 1f13ab7ec6
commit f9512cbb18
33 changed files with 10956 additions and 1 deletions

View File

@ -5,6 +5,8 @@ VisualStudioVersion = 16.0.29009.5
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "millgame", "millgame.vcxproj", "{D6EBE2B6-17F9-30EA-AE68-9CD0BB526200}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "perfect", "src\perfect\perfect.vcxproj", "{EDB1E279-1476-443B-84FA-150A5D3B5A10}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
@ -21,12 +23,20 @@ Global
{D6EBE2B6-17F9-30EA-AE68-9CD0BB526200}.Release|x64.Build.0 = Release|x64
{D6EBE2B6-17F9-30EA-AE68-9CD0BB526200}.Release|x86.ActiveCfg = Release|Win32
{D6EBE2B6-17F9-30EA-AE68-9CD0BB526200}.Release|x86.Build.0 = Release|Win32
{EDB1E279-1476-443B-84FA-150A5D3B5A10}.Debug|x64.ActiveCfg = Debug|x64
{EDB1E279-1476-443B-84FA-150A5D3B5A10}.Debug|x64.Build.0 = Debug|x64
{EDB1E279-1476-443B-84FA-150A5D3B5A10}.Debug|x86.ActiveCfg = Debug|Win32
{EDB1E279-1476-443B-84FA-150A5D3B5A10}.Debug|x86.Build.0 = Debug|Win32
{EDB1E279-1476-443B-84FA-150A5D3B5A10}.Release|x64.ActiveCfg = Release|x64
{EDB1E279-1476-443B-84FA-150A5D3B5A10}.Release|x64.Build.0 = Release|x64
{EDB1E279-1476-443B-84FA-150A5D3B5A10}.Release|x86.ActiveCfg = Release|Win32
{EDB1E279-1476-443B-84FA-150A5D3B5A10}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {401C61DF-0B94-45A9-96C1-9BD069796A84}
Qt5Version = Qt5.13.0
SolutionGuid = {401C61DF-0B94-45A9-96C1-9BD069796A84}
EndGlobalSection
EndGlobal

150
perfect/perfect.vcxproj Normal file
View File

@ -0,0 +1,150 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
<ProjectGuid>{462a21df-6c88-4e69-ba15-ab16636cdd1b}</ProjectGuid>
<RootNamespace>perfect</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared" >
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemGroup></ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
</Project>

View File

@ -0,0 +1,217 @@
/*********************************************************************
bufferedFile.cpp
Copyright (c) Thomas Weber. All rights reserved.
Licensed under the MIT License.
https://github.com/madweasel/madweasels-cpp
\*********************************************************************/
#include "bufferedFile.h"
//-----------------------------------------------------------------------------
// Name: bufferedFile()
// Desc: Creates a cyclic array. The passed file is used as temporary data buffer for the cyclic array.
//-----------------------------------------------------------------------------
bufferedFileClass::bufferedFileClass(unsigned int numberOfThreads, unsigned int bufferSizeInBytes, const char *fileName)
{
// locals
unsigned int curThread;
// Init blocks
bufferSize = bufferSizeInBytes;
numThreads = numberOfThreads;
readBuffer = new unsigned char [numThreads*bufferSize];
writeBuffer = new unsigned char [numThreads*bufferSize];
curWritingPointer = new long long [numThreads];
curReadingPointer = new long long [numThreads];
bytesInReadBuffer = new unsigned int [numThreads];
bytesInWriteBuffer = new unsigned int [numThreads];
for (curThread=0; curThread<numThreads; curThread++) {
curReadingPointer[curThread] = 0;
curWritingPointer[curThread] = 0;
bytesInReadBuffer [curThread] = 0;
bytesInWriteBuffer[curThread] = 0;
}
InitializeCriticalSection(&csIO);
// Open Database-File (FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH | FILE_FLAG_RANDOM_ACCESS)
hFile = CreateFileA(fileName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
// opened file succesfully
if (hFile == INVALID_HANDLE_VALUE) {
hFile = NULL;
return;
}
// update file size
getFileSize();
}
//-----------------------------------------------------------------------------
// Name: ~bufferedFileClass()
// Desc: bufferedFileClass class destructor
//-----------------------------------------------------------------------------
bufferedFileClass::~bufferedFileClass()
{
// flush buffers
flushBuffers();
DeleteCriticalSection(&csIO);
// delete arrays
delete [] readBuffer;
delete [] writeBuffer;
delete [] curReadingPointer;
delete [] curWritingPointer;
delete [] bytesInReadBuffer ;
delete [] bytesInWriteBuffer;
// close file
if (hFile != NULL) CloseHandle(hFile);
}
//-----------------------------------------------------------------------------
// Name: getFileSize()
// Desc:
//-----------------------------------------------------------------------------
long long bufferedFileClass::getFileSize()
{
LARGE_INTEGER liFileSize;
GetFileSizeEx(hFile, &liFileSize);
fileSize = liFileSize.QuadPart;
return fileSize;
}
//-----------------------------------------------------------------------------
// Name: flushBuffers()
// Desc:
//-----------------------------------------------------------------------------
bool bufferedFileClass::flushBuffers()
{
for (unsigned int threadNo=0; threadNo<numThreads; threadNo++) {
writeDataToFile(hFile, curWritingPointer[threadNo] - bytesInWriteBuffer[threadNo], bytesInWriteBuffer[threadNo], &writeBuffer[threadNo*bufferSize+0]);
bytesInWriteBuffer[threadNo] = 0;
}
return true;
}
//-----------------------------------------------------------------------------
// Name: writeDataToFile()
// Desc: Writes 'sizeInBytes'-bytes to the position 'offset' to the file.
//-----------------------------------------------------------------------------
void bufferedFileClass::writeDataToFile(HANDLE hFile, long long offset, unsigned int sizeInBytes, void *pData)
{
DWORD dwBytesWritten;
LARGE_INTEGER liDistanceToMove;
unsigned int restingBytes = sizeInBytes;
liDistanceToMove.QuadPart = offset;
EnterCriticalSection(&csIO);
while (!SetFilePointerEx(hFile, liDistanceToMove, NULL, FILE_BEGIN)) cout << endl << "SetFilePointerEx failed!";
while (restingBytes > 0) {
if (WriteFile(hFile, pData, sizeInBytes, &dwBytesWritten, NULL) == TRUE) {
restingBytes -= dwBytesWritten;
pData = (void*) (((unsigned char*) pData) + dwBytesWritten);
if (restingBytes > 0) cout << endl << "Still " << restingBytes << " to write!";
} else {
cout << endl << "WriteFile Failed!";
}
}
LeaveCriticalSection(&csIO);
}
//-----------------------------------------------------------------------------
// Name: readDataFromFile()
// Desc: Reads 'sizeInBytes'-bytes from the position 'offset' of the file.
//-----------------------------------------------------------------------------
void bufferedFileClass::readDataFromFile(HANDLE hFile, long long offset, unsigned int sizeInBytes, void *pData)
{
DWORD dwBytesRead;
LARGE_INTEGER liDistanceToMove;
unsigned int restingBytes = sizeInBytes;
liDistanceToMove.QuadPart = offset;
EnterCriticalSection(&csIO);
while (!SetFilePointerEx(hFile, liDistanceToMove, NULL, FILE_BEGIN)) cout << endl << "SetFilePointerEx failed!";
while (restingBytes > 0) {
if (ReadFile(hFile, pData, sizeInBytes, &dwBytesRead, NULL) == TRUE) {
restingBytes -= dwBytesRead;
pData = (void*) (((unsigned char*) pData) + dwBytesRead);
if (restingBytes > 0) cout << endl << "Still " << restingBytes << " to read!";
} else {
cout << endl << "ReadFile Failed!";
}
}
LeaveCriticalSection(&csIO);
}
//-----------------------------------------------------------------------------
// Name: writeBytes()
// Desc:
//-----------------------------------------------------------------------------
bool bufferedFileClass::writeBytes(unsigned int numBytes, unsigned char* pData)
{
return writeBytes(0, curWritingPointer[0], numBytes, pData);
}
//-----------------------------------------------------------------------------
// Name: writeBytes()
// Desc:
//-----------------------------------------------------------------------------
bool bufferedFileClass::writeBytes(unsigned int threadNo, long long positionInFile, unsigned int numBytes, unsigned char* pData)
{
// parameters ok?
if (threadNo >= numThreads) return false;
if (pData == NULL) return false;
// locals
// if buffer full or not sequential write operation write buffer to file
if (bytesInWriteBuffer[threadNo] && (positionInFile != curWritingPointer[threadNo] || bytesInWriteBuffer[threadNo] + numBytes >= bufferSize)) {
writeDataToFile(hFile, curWritingPointer[threadNo] - bytesInWriteBuffer[threadNo], bytesInWriteBuffer[threadNo], &writeBuffer[threadNo*bufferSize+0]);
bytesInWriteBuffer[threadNo] = 0;
}
// copy data into buffer
memcpy(&writeBuffer[threadNo*bufferSize+bytesInWriteBuffer[threadNo]], pData, numBytes);
bytesInWriteBuffer[threadNo] += numBytes;
curWritingPointer[threadNo] = positionInFile + numBytes;
// everything ok
return true;
}
//-----------------------------------------------------------------------------
// Name: takeBytes()
// Desc:
//-----------------------------------------------------------------------------
bool bufferedFileClass::readBytes(unsigned int numBytes, unsigned char* pData)
{
return readBytes(0, curReadingPointer[0], numBytes, pData);
}
//-----------------------------------------------------------------------------
// Name: takeBytes()
// Desc:
//-----------------------------------------------------------------------------
bool bufferedFileClass::readBytes(unsigned int threadNo, long long positionInFile, unsigned int numBytes, unsigned char* pData)
{
// parameters ok?
if (threadNo >= numThreads) return false;
if (pData == NULL) return false;
// read from file into buffer if not enough data in buffer or if it is not an sequential reading operation?
if (positionInFile != curReadingPointer[threadNo] || bytesInReadBuffer[threadNo] < numBytes) {
bytesInReadBuffer[threadNo] = ((positionInFile + bufferSize <= fileSize) ? bufferSize : (unsigned int) (fileSize - positionInFile));
if (bytesInReadBuffer[threadNo] < numBytes) return false;
readDataFromFile(hFile, positionInFile, bytesInReadBuffer[threadNo], &readBuffer[threadNo*bufferSize+bufferSize-bytesInReadBuffer[threadNo]]);
}
memcpy(pData, &readBuffer[threadNo*bufferSize+bufferSize-bytesInReadBuffer[threadNo]], numBytes);
bytesInReadBuffer[threadNo] -= numBytes;
curReadingPointer[threadNo] = positionInFile + numBytes;
// everything ok
return true;
}

View File

@ -0,0 +1,52 @@
/*********************************************************************\
bufferedFile.h
Copyright (c) Thomas Weber. All rights reserved.
Licensed under the MIT License.
https://github.com/madweasel/madweasels-cpp
\*********************************************************************/
#ifndef BUFFERED_FILE_H
#define BUFFERED_FILE_H
#include <windows.h>
#include <iostream>
#include <string>
using namespace std;
/*** Klassen *********************************************************/
class bufferedFileClass
{
private:
// Variables
HANDLE hFile; // Handle of the file
unsigned int numThreads; // number of threads
unsigned char * readBuffer; // Array of size [numThreads*blockSize] containing the data of the block, where reading is taking place
unsigned char * writeBuffer; // '' - access by [threadNo*bufferSize+position]
long long * curReadingPointer; // array of size [numThreads] with pointers to the byte which is currently read
long long * curWritingPointer; // ''
unsigned int * bytesInReadBuffer; //
unsigned int * bytesInWriteBuffer; //
unsigned int bufferSize; // size in bytes of a buffer
long long fileSize; // size in bytes
CRITICAL_SECTION csIO;
// Functions
void writeDataToFile (HANDLE hFile, long long offset, unsigned int sizeInBytes, void *pData);
void readDataFromFile (HANDLE hFile, long long offset, unsigned int sizeInBytes, void *pData);
public:
// Constructor / destructor
bufferedFileClass (unsigned int numThreads, unsigned int bufferSizeInBytes, const char *fileName);
~bufferedFileClass ();
// Functions
bool flushBuffers ();
bool writeBytes (unsigned int numBytes, unsigned char* pData);
bool readBytes (unsigned int numBytes, unsigned char* pData);
bool writeBytes (unsigned int threadNo, long long positionInFile, unsigned int numBytes, unsigned char* pData);
bool readBytes (unsigned int threadNo, long long positionInFile, unsigned int numBytes, unsigned char* pData);
long long getFileSize ();
};
#endif

124
src/perfect/console.cpp Normal file
View File

@ -0,0 +1,124 @@
#include <cstdio>
#include <iostream>
#include <windows.h>
#include "muehle.h"
#include "minMaxKI.h"
#include "randomKI.h"
#include "perfectKI.h"
using namespace std;
unsigned int startTestFromLayer = 0;
unsigned int endTestAtLayer = NUM_LAYERS-1;
#ifdef _DEBUG
char databaseDirectory[] = ".";
#elif _RELEASE_X64
char databaseDirectory[] = "";
#endif
bool calculateDatabase = false;
void main(void)
{
// locals
bool playerOneHuman = false;
bool playerTwoHuman = false;
char tmpChar[100];
unsigned int pushFrom, pushTo;
muehle* myGame = new muehle();
perfectKI* myKI = new perfectKI(databaseDirectory);
SetPriorityClass(GetCurrentProcess(), BELOW_NORMAL_PRIORITY_CLASS);
srand(GetTickCount());
// intro
cout << "*************************" << endl;
cout << "* Muehle *" << endl;
cout << "*************************" << endl << endl;
myKI->setDatabasePath(databaseDirectory);
// begin
myGame->beginNewGame(myKI, myKI, (rand() % 2) ? fieldStruct::playerOne : fieldStruct::playerTwo);
if (calculateDatabase) {
// calculate
myKI->calculateDatabase(MAX_DEPTH_OF_TREE, false);
// test database
cout << endl << "Begin test starting from layer: "; startTestFromLayer;
cout << endl << "End test at layer: "; endTestAtLayer;
myKI->testLayers(startTestFromLayer, endTestAtLayer);
} else {
cout << "Is Player 1 human? (y/n):"; cin >> tmpChar; if (tmpChar[0] == 'y') playerOneHuman = true;
cout << "Is Player 2 human? (y/n):"; cin >> tmpChar; if (tmpChar[0] == 'y') playerTwoHuman = true;
// play
do
{
// print field
cout << "\n\n\n\n\n\n\n\n\n\n\n";
myGame->getComputersChoice(&pushFrom, &pushTo);
cout << "\n\n";
cout << "\nlast move was from " << (char)(myGame->getLastMoveFrom() + 97) << " to " << (char)(myGame->getLastMoveTo() + 97) << "\n\n";
myGame->printField();
// Human
if ((myGame->getCurrentPlayer() == fieldStruct::playerOne && playerOneHuman)
|| (myGame->getCurrentPlayer() == fieldStruct::playerTwo && playerTwoHuman)) {
do {
// Show text
if (myGame->mustStoneBeRemoved()) cout << "\n Which stone do you want to remove? [a-x]: \n\n\n";
else if (myGame->inSettingPhase()) cout << "\n Where are you going? [a-x]: \n\n\n";
else cout << "\n Your train? [a-x][a-x]: \n\n\n";
// get input
cin >> tmpChar;
if ((tmpChar[0] >= 'a') && (tmpChar[0] <= 'x')) pushFrom = tmpChar[0] - 'a'; else pushFrom = fieldStruct::size;
if (myGame->inSettingPhase()) {
if ((tmpChar[0] >= 'a') && (tmpChar[0] <= 'x')) pushTo = tmpChar[0] - 'a'; else pushTo = fieldStruct::size;
} else {
if ((tmpChar[1] >= 'a') && (tmpChar[1] <= 'x')) pushTo = tmpChar[1] - 'a'; else pushTo = fieldStruct::size;
}
// undo
if (tmpChar[0] == 'u' && tmpChar[1] == 'n' && tmpChar[2] == 'd' && tmpChar[3] == 'o') {
// undo moves until a human player shall move
do {
myGame->undoLastMove();
} while (!((myGame->getCurrentPlayer() == fieldStruct::playerOne && playerOneHuman)
|| (myGame->getCurrentPlayer() == fieldStruct::playerTwo && playerTwoHuman)));
// reprint field
break;
}
} while (myGame->moveStone(pushFrom, pushTo) == false);
// Computer
} else {
cout << "\n";
myGame->moveStone(pushFrom, pushTo);
}
} while (myGame->getWinner() == 0);
// end
cout << "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n";
myGame->printField();
if (myGame->getWinner() == fieldStruct::playerOne) cout << "\n Player 1 (o) won after " << myGame->getMovesDone() << " move.\n\n";
else if (myGame->getWinner() == fieldStruct::playerTwo) cout << "\n Player 2 (x) won after " << myGame->getMovesDone() << " move.\n\n";
else if (myGame->getWinner() == fieldStruct::gameDrawn) cout << "\n Draw!\n\n";
else cout << "\n A program error has occurred!\n\n";
}
char end;
cin >> end;
}

342
src/perfect/cyclicArray.cpp Normal file
View File

@ -0,0 +1,342 @@
/*********************************************************************
cyclicArray.cpp
Copyright (c) Thomas Weber. All rights reserved.
Licensed under the MIT License.
https://github.com/madweasel/madweasels-cpp
\*********************************************************************/
#include "cyclicArray.h"
//-----------------------------------------------------------------------------
// Name: cyclicArray()
// Desc: Creates a cyclic array. The passed file is used as temporary data buffer for the cyclic array.
//-----------------------------------------------------------------------------
cyclicArray::cyclicArray(unsigned int blockSizeInBytes, unsigned int numberOfBlocks, const char *fileName)
{
// Init blocks
blockSize = blockSizeInBytes;
numBlocks = numberOfBlocks;
readingBlock = new unsigned char[blockSize];
writingBlock = new unsigned char[blockSize];
curReadingPointer = writingBlock;
curWritingPointer = writingBlock;
readWriteInSameRound= true;
curReadingBlock = 0;
curWritingBlock = 0;
// Open Database-File (FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH | FILE_FLAG_RANDOM_ACCESS)
hFile = CreateFileA(fileName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
// opened file succesfully
if (hFile == INVALID_HANDLE_VALUE) {
hFile = NULL;
return;
}
}
//-----------------------------------------------------------------------------
// Name: ~randomKI()
// Desc: randomKI class destructor
//-----------------------------------------------------------------------------
cyclicArray::~cyclicArray()
{
// delete arrays
delete [] readingBlock;
delete [] writingBlock;
// close file
if (hFile != NULL) CloseHandle(hFile);
}
//-----------------------------------------------------------------------------
// Name: writeDataToFile()
// Desc: Writes 'sizeInBytes'-bytes to the position 'offset' to the file.
//-----------------------------------------------------------------------------
void cyclicArray::writeDataToFile(HANDLE hFile, long long offset, unsigned int sizeInBytes, void *pData)
{
DWORD dwBytesWritten;
LARGE_INTEGER liDistanceToMove;
unsigned int restingBytes = sizeInBytes;
liDistanceToMove.QuadPart = offset;
while (!SetFilePointerEx(hFile, liDistanceToMove, NULL, FILE_BEGIN)) cout << endl << "SetFilePointerEx failed!";
while (restingBytes > 0) {
if (WriteFile(hFile, pData, sizeInBytes, &dwBytesWritten, NULL) == TRUE) {
restingBytes -= dwBytesWritten;
pData = (void*) (((unsigned char*) pData) + dwBytesWritten);
if (restingBytes > 0) cout << endl << "Still " << restingBytes << " to write!";
} else {
cout << endl << "WriteFile Failed!";
}
}
}
//-----------------------------------------------------------------------------
// Name: readDataFromFile()
// Desc: Reads 'sizeInBytes'-bytes from the position 'offset' of the file.
//-----------------------------------------------------------------------------
void cyclicArray::readDataFromFile(HANDLE hFile, long long offset, unsigned int sizeInBytes, void *pData)
{
DWORD dwBytesRead;
LARGE_INTEGER liDistanceToMove;
unsigned int restingBytes = sizeInBytes;
liDistanceToMove.QuadPart = offset;
while (!SetFilePointerEx(hFile, liDistanceToMove, NULL, FILE_BEGIN)) cout << endl << "SetFilePointerEx failed!";
while (restingBytes > 0) {
if (ReadFile(hFile, pData, sizeInBytes, &dwBytesRead, NULL) == TRUE) {
restingBytes -= dwBytesRead;
pData = (void*) (((unsigned char*) pData) + dwBytesRead);
if (restingBytes > 0) cout << endl << "Still " << restingBytes << " to read!";
} else {
cout << endl << "ReadFile Failed!";
}
}
}
//-----------------------------------------------------------------------------
// Name: addBytes()
// Desc: Add the passed data to the cyclic array. If the writing pointer reaches the end of a block,
// the data of the whole block is written to the file and the next block is considered for writing.
//-----------------------------------------------------------------------------
bool cyclicArray::addBytes(unsigned int numBytes, unsigned char* pData)
{
// locals
unsigned int bytesWritten = 0;
// write each byte
while (bytesWritten < numBytes) {
// store byte in current reading block
*curWritingPointer = *pData;
curWritingPointer++;
bytesWritten++;
pData++;
// when block is full then save current one to file and begin new one
if (curWritingPointer == writingBlock + blockSize) {
// copy data into reading block?
if (curReadingBlock == curWritingBlock) {
memcpy(readingBlock, writingBlock, blockSize);
curReadingPointer = readingBlock + (curReadingPointer - writingBlock);
}
// will reading block be overwritten?
if (curReadingBlock == curWritingBlock && !readWriteInSameRound) return false;
// store bock in file
writeDataToFile(hFile, ((long long) blockSize) * ((long long) curWritingBlock), blockSize, writingBlock);
// set pointer to beginnig of writing block
curWritingPointer = writingBlock;
curWritingBlock = (curWritingBlock + 1) % numBlocks;
if (curWritingBlock == 0) readWriteInSameRound = false;
}
}
// everything ok
return true;
}
//-----------------------------------------------------------------------------
// Name: bytesAvailable()
// Desc:
//-----------------------------------------------------------------------------
bool cyclicArray::bytesAvailable()
{
if (curReadingBlock == curWritingBlock && curReadingPointer == curWritingPointer && readWriteInSameRound) return false;
else return true;
}
//-----------------------------------------------------------------------------
// Name: takeBytes()
// Desc: Load data from the cyclic array. If the reading pointer reaches the end of a block,
// the data of the next whole block is read from the file.
//-----------------------------------------------------------------------------
bool cyclicArray::takeBytes(unsigned int numBytes, unsigned char* pData)
{
// locals
unsigned int bytesRead = 0;
// read each byte
while (bytesRead < numBytes) {
// was current reading byte already written ?
if (curReadingBlock == curWritingBlock && curReadingPointer == curWritingPointer && readWriteInSameRound) return false;
// read current byte
*pData = *curReadingPointer;
curReadingPointer++;
bytesRead++;
pData++;
// load next block?
if (curReadingPointer == readingBlock + blockSize) {
// go to next block
curReadingBlock = (curReadingBlock + 1) % numBlocks;
if (curReadingBlock == 0) readWriteInSameRound = true;
// writing block reached ?
if (curReadingBlock == curWritingBlock) {
curReadingPointer = writingBlock;
} else {
// set pointer to beginnig of reading block
curReadingPointer = readingBlock;
// read whole block from file
readDataFromFile(hFile, ((long long) blockSize) * ((long long) curReadingBlock), blockSize, readingBlock);
}
}
}
// everything ok
return true;
}
//-----------------------------------------------------------------------------
// Name: loadFile()
// Desc: Load the passed file into the cyclic array.
// The passed filename must be different than the passed filename to the constructor cyclicarray().
//-----------------------------------------------------------------------------
bool cyclicArray::loadFile(const char *fileName, LONGLONG &numBytesLoaded)
{
// locals
HANDLE hLoadFile;
unsigned char* dataInFile;
LARGE_INTEGER largeInt;
LONGLONG maxFileSize = ((LONGLONG) blockSize) * ((LONGLONG) numBlocks);
LONGLONG curOffset = 0;
unsigned int numBlocksInFile;
unsigned int curBlock;
unsigned int numBytesInLastBlock;
numBytesLoaded = 0;
// cyclic array file must be open
if (hFile == NULL) return false;
// Open Database-File (FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH | FILE_FLAG_RANDOM_ACCESS)
hLoadFile = CreateFileA(fileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
// opened file succesfully
if (hLoadFile == INVALID_HANDLE_VALUE) {
return false;
}
// does data of file fit into cyclic array ?
GetFileSizeEx(hLoadFile, &largeInt);
if (maxFileSize < largeInt.QuadPart) {
CloseHandle(hLoadFile);
return false;
}
// reset
curReadingPointer = writingBlock;
curWritingPointer = writingBlock;
readWriteInSameRound= true;
curReadingBlock = 0;
curWritingBlock = 0;
numBlocksInFile = (unsigned int) (largeInt.QuadPart / ((LONGLONG)blockSize)) + 1;
numBytesInLastBlock = (unsigned int) (largeInt.QuadPart % ((LONGLONG)blockSize));
dataInFile = new unsigned char[blockSize];
//
for (curBlock=0; curBlock<numBlocksInFile-1; curBlock++, curOffset += blockSize) {
// load data from file
readDataFromFile(hLoadFile, curOffset, blockSize, dataInFile);
// put block in cyclic array
addBytes(blockSize, dataInFile);
}
// last block
readDataFromFile(hLoadFile, curOffset, numBytesInLastBlock, dataInFile);
addBytes(numBytesInLastBlock, dataInFile);
curOffset += numBytesInLastBlock;
numBytesLoaded = curOffset;
// everything ok
delete [] dataInFile;
CloseHandle(hLoadFile);
return true;
}
//-----------------------------------------------------------------------------
// Name: saveFile()
// Desc: Writes the whole current content of the cyclic array to the passed file.
// The passed filename must be different than the passed filename to the constructor cyclicarray().
//-----------------------------------------------------------------------------
bool cyclicArray::saveFile(const char *fileName)
{
// locals
unsigned char* dataInFile;
HANDLE hSaveFile;
LONGLONG curOffset;
unsigned int curBlock;
unsigned int bytesToWrite;
void * pointer;
// cyclic array file must be open
if (hFile == NULL) {
return false;
}
// Open Database-File (FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH | FILE_FLAG_RANDOM_ACCESS)
hSaveFile = CreateFileA(fileName, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
// opened file succesfully
if (hSaveFile == INVALID_HANDLE_VALUE) {
return false;
}
// alloc mem
curOffset = 0;
curBlock = curReadingBlock;
dataInFile = new unsigned char[blockSize];
do {
// copy current block
if (curBlock == curWritingBlock && curBlock == curReadingBlock) {
pointer = curReadingPointer;
bytesToWrite = (unsigned int) (curWritingPointer - curReadingPointer);
} else if (curBlock == curWritingBlock) {
pointer = writingBlock;
bytesToWrite = (unsigned int) (curWritingPointer - writingBlock);
} else if (curBlock == curReadingBlock) {
pointer = curReadingPointer;
bytesToWrite = blockSize - (unsigned int) (curReadingPointer - readingBlock);
} else {
readDataFromFile(hFile, ((long long) curBlock) * ((long long) blockSize), blockSize, dataInFile);
pointer = dataInFile;
bytesToWrite = blockSize;
}
// save data to file
writeDataToFile(hSaveFile, curOffset, bytesToWrite, pointer);
curOffset += bytesToWrite;
// exit?
if (curBlock == curWritingBlock) break;
else curBlock = (curBlock+1) % numBlocks;
} while (true);
// everything ok
delete [] dataInFile;
CloseHandle(hSaveFile);
return true;
}

50
src/perfect/cyclicArray.h Normal file
View File

@ -0,0 +1,50 @@
/*********************************************************************\
cyclicArray.h
Copyright (c) Thomas Weber. All rights reserved.
Licensed under the MIT License.
https://github.com/madweasel/madweasels-cpp
\*********************************************************************/
#ifndef CYLCIC_ARRAY_H
#define CYLCIC_ARRAY_H
#include <windows.h>
#include <iostream>
#include <string>
using namespace std;
/*** Klassen *********************************************************/
class cyclicArray
{
private:
// Variables
HANDLE hFile; // Handle of the file
unsigned char* readingBlock; // Array of size [blockSize] containing the data of the block, where reading is taking place
unsigned char* writingBlock; // ''
unsigned char* curReadingPointer; // pointer to the byte which is currently read
unsigned char* curWritingPointer; // ''
unsigned int blockSize; // size in bytes of a block
unsigned int curReadingBlock; // index of the block, where reading is taking place
unsigned int curWritingBlock; // index of the block, where writing is taking place
unsigned int numBlocks; // amount of blocks
bool readWriteInSameRound; // true if curReadingBlock > curWritingBlock, false otherwise
// Functions
void writeDataToFile (HANDLE hFile, long long offset, unsigned int sizeInBytes, void *pData);
void readDataFromFile (HANDLE hFile, long long offset, unsigned int sizeInBytes, void *pData);
public:
// Constructor / destructor
cyclicArray (unsigned int blockSizeInBytes, unsigned int numberOfBlocks, const char *fileName);
~cyclicArray ();
// Functions
bool addBytes (unsigned int numBytes, unsigned char* pData);
bool takeBytes (unsigned int numBytes, unsigned char* pData);
bool loadFile (const char *fileName, LONGLONG &numBytesLoaded);
bool saveFile (const char *fileName);
bool bytesAvailable ();
};
#endif

550
src/perfect/minMaxKI.cpp Normal file
View File

@ -0,0 +1,550 @@
/*********************************************************************
minMaxKI.cpp
Copyright (c) Thomas Weber. All rights reserved.
Licensed under the MIT License.
https://github.com/madweasel/madweasels-cpp
\*********************************************************************/
#include "minMaxKI.h"
//-----------------------------------------------------------------------------
// Name: minMaxKI()
// Desc: minMaxKI class constructor
//-----------------------------------------------------------------------------
minMaxKI::minMaxKI()
{
depthOfFullTree = 0;
}
//-----------------------------------------------------------------------------
// Name: ~minMaxKI()
// Desc: minMaxKI class destructor
//-----------------------------------------------------------------------------
minMaxKI::~minMaxKI()
{
}
//-----------------------------------------------------------------------------
// Name: play()
// Desc:
//-----------------------------------------------------------------------------
void minMaxKI::play(fieldStruct *theField, unsigned int *pushFrom, unsigned int *pushTo)
{
// globals
field = theField;
ownId = field->curPlayer->id;
curSearchDepth = 0;
unsigned int bestChoice;
unsigned int searchDepth;
// automatic depth
if (depthOfFullTree == 0) {
if (theField->settingPhase) searchDepth = 5;
else if (theField->curPlayer->numStones <= 4) searchDepth = 7;
else if (theField->oppPlayer->numStones <= 4) searchDepth = 7;
else searchDepth = 7;
} else {
searchDepth = depthOfFullTree;
}
// Inform user about progress
cout << "minMaxKI is thinking with a depth of " << searchDepth << " steps!\n\n\n";
// reserve memory
possibilities = new possibilityStruct [ searchDepth + 1];
oldStates = new backupStruct [ searchDepth + 1];
idPossibilities = new unsigned int [(searchDepth + 1) * MAX_NUM_POS_MOVES];
// start the miniMax-algorithmn
possibilityStruct *rootPossibilities = (possibilityStruct*) getBestChoice(searchDepth, &bestChoice, MAX_NUM_POS_MOVES);
// decode the best choice
if (field->stoneMustBeRemoved) { *pushFrom = bestChoice; *pushTo = 0; }
else if (field->settingPhase) { *pushFrom = 0; *pushTo = bestChoice; }
else { *pushFrom = rootPossibilities->from[bestChoice];
*pushTo = rootPossibilities->to [bestChoice]; }
// release memory
delete [] oldStates;
delete [] idPossibilities;
delete [] possibilities;
// release memory
field = NULL;
}
//-----------------------------------------------------------------------------
// Name: setSearchDepth()
// Desc:
//-----------------------------------------------------------------------------
void minMaxKI::setSearchDepth(unsigned int depth)
{
depthOfFullTree = depth;
}
//-----------------------------------------------------------------------------
// Name: prepareBestChoiceCalculation()
// Desc:
//-----------------------------------------------------------------------------
void minMaxKI::prepareBestChoiceCalculation()
{
// calculate current value
currentValue = 0;
gameHasFinished = false;
}
//-----------------------------------------------------------------------------
// Name: getPossSettingPhase()
// Desc:
//-----------------------------------------------------------------------------
unsigned int *minMaxKI::getPossSettingPhase(unsigned int *numPossibilities, void **pPossibilities)
{
// locals
unsigned int i;
unsigned int *idPossibility = &idPossibilities[curSearchDepth * MAX_NUM_POS_MOVES];
// possibilities with cut off
for ((*numPossibilities) = 0, i=0; i<field->size; i++) {
// move possible ?
if (field->field[i] == field->squareIsFree) {
idPossibility[*numPossibilities] = i;
(*numPossibilities)++;
}
}
// possibility code is simple
*pPossibilities = NULL;
return idPossibility;
}
//-----------------------------------------------------------------------------
// Name: getPossNormalMove()
// Desc:
//-----------------------------------------------------------------------------
unsigned int * minMaxKI::getPossNormalMove(unsigned int *numPossibilities, void **pPossibilities)
{
// locals
unsigned int from, to, dir;
unsigned int *idPossibility = &idPossibilities[curSearchDepth * MAX_NUM_POS_MOVES];
possibilityStruct *possibility = &possibilities [curSearchDepth];
// if he is not allowed to spring
if (field->curPlayer->numStones > 3) {
for ((*numPossibilities) = 0, from=0; from < field->size; from++) { for (dir=0; dir<4; dir++) {
// destination
to = field->connectedSquare[from][dir];
// move possible ?
if (to < field->size && field->field[from] == field->curPlayer->id && field->field[to] == field->squareIsFree) {
// stone is moveable
idPossibility[*numPossibilities] = *numPossibilities;
possibility->from[*numPossibilities] = from;
possibility->to[*numPossibilities] = to;
(*numPossibilities)++;
// current player is allowed to spring
}}}} else {
for ((*numPossibilities) = 0, from=0; from < field->size; from++) { for (to=0; to < field->size; to++) {
// move possible ?
if (field->field[from] == field->curPlayer->id && field->field[to] == field->squareIsFree && *numPossibilities < MAX_NUM_POS_MOVES) {
// stone is moveable
idPossibility[*numPossibilities] = *numPossibilities;
possibility->from[*numPossibilities] = from;
possibility->to[*numPossibilities] = to;
(*numPossibilities)++;
}}}}
// pass possibilities
*pPossibilities = (void*)possibility;
return idPossibility;
}
//-----------------------------------------------------------------------------
// Name: getPossStoneRemove()
// Desc:
//-----------------------------------------------------------------------------
unsigned int * minMaxKI::getPossStoneRemove(unsigned int *numPossibilities, void **pPossibilities)
{
// locals
unsigned int i;
unsigned int *idPossibility = &idPossibilities[curSearchDepth * MAX_NUM_POS_MOVES];
// possibilities with cut off
for ((*numPossibilities) = 0, i=0; i<field->size; i++) {
// move possible ?
if (field->field[i] == field->oppPlayer->id && !field->stonePartOfMill[i]) {
idPossibility[*numPossibilities] = i;
(*numPossibilities)++;
}
}
// possibility code is simple
*pPossibilities = NULL;
return idPossibility;
}
//-----------------------------------------------------------------------------
// Name: getPossibilities()
// Desc:
//-----------------------------------------------------------------------------
unsigned int * minMaxKI::getPossibilities(unsigned int threadNo, unsigned int *numPossibilities, bool *opponentsMove, void **pPossibilities)
{
// set opponentsMove
*opponentsMove = (field->curPlayer->id == ownId) ? false : true;
// When game has ended of course nothing happens any more
if (gameHasFinished) {
*numPossibilities = 0;
return 0;
// look what is to do
} else {
if (field->stoneMustBeRemoved) return getPossStoneRemove (numPossibilities, pPossibilities);
else if (field->settingPhase) return getPossSettingPhase (numPossibilities, pPossibilities);
else return getPossNormalMove (numPossibilities, pPossibilities);
}
}
//-----------------------------------------------------------------------------
// Name: getValueOfSituation()
// Desc:
//-----------------------------------------------------------------------------
void minMaxKI::getValueOfSituation(unsigned int threadNo, float &floatValue, twoBit &shortValue)
{
floatValue = currentValue;
shortValue = 0;
}
//-----------------------------------------------------------------------------
// Name: deletePossibilities()
// Desc:
//-----------------------------------------------------------------------------
void minMaxKI::deletePossibilities(unsigned int threadNo, void *pPossibilities)
{
}
//-----------------------------------------------------------------------------
// Name: undo()
// Desc:
//-----------------------------------------------------------------------------
void minMaxKI::undo(unsigned int threadNo, unsigned int idPossibility, bool opponentsMove, void *pBackup, void *pPossibilities)
{
// locals
backupStruct *oldState = (backupStruct*)pBackup;
// reset old value
currentValue = oldState->value;
gameHasFinished = oldState->gameHasFinished;
curSearchDepth--;
field->curPlayer = oldState->curPlayer;
field->oppPlayer = oldState->oppPlayer;
field->curPlayer->numStones = oldState->curNumStones;
field->oppPlayer->numStones = oldState->oppNumStones;
field->curPlayer->numStonesMissing = oldState->curMissStones;
field->oppPlayer->numStonesMissing = oldState->oppMissStones;
field->curPlayer->numPossibleMoves = oldState->curPosMoves;
field->oppPlayer->numPossibleMoves = oldState->oppPosMoves;
field->settingPhase = oldState->settingPhase;
field->stonesSet = oldState->stonesSet;
field->stoneMustBeRemoved = oldState->stoneMustBeRemoved;
field->field[oldState->from] = oldState->fieldFrom;
field->field[oldState->to ] = oldState->fieldTo;
// very expensive
for (int i=0; i<field->size; i++) {
field->stonePartOfMill[i] = oldState->stonePartOfMill[i];
field->warnings[i] = oldState->warnings[i];
}
}
//-----------------------------------------------------------------------------
// Name: setWarning()
// Desc:
//-----------------------------------------------------------------------------
inline void minMaxKI::setWarning(unsigned int stoneOne, unsigned int stoneTwo, unsigned int stoneThree)
{
// if all 3 fields are occupied by current player than he closed a mill
if (field->field[stoneOne] == field->curPlayer->id && field->field[stoneTwo] == field->curPlayer->id && field->field[stoneThree] == field->curPlayer->id) {
field->stonePartOfMill[stoneOne ]++;
field->stonePartOfMill[stoneTwo ]++;
field->stonePartOfMill[stoneThree]++;
field->stoneMustBeRemoved = 1;
}
// is a mill destroyed ?
if (field->field[stoneOne] == field->squareIsFree && field->stonePartOfMill[stoneOne] && field->stonePartOfMill[stoneTwo] && field->stonePartOfMill[stoneThree]) {
field->stonePartOfMill[stoneOne ]--;
field->stonePartOfMill[stoneTwo ]--;
field->stonePartOfMill[stoneThree]--;
}
// stone was set
if (field->field[stoneOne] == field->curPlayer->id) {
// a warnig was destroyed
field->warnings[stoneOne] = field->noWarning;
// a warning is created
if (field->field[stoneTwo ] == field->curPlayer->id && field->field[stoneThree] == field->squareIsFree) field->warnings[stoneThree] |= field->curPlayer->warning;
if (field->field[stoneThree] == field->curPlayer->id && field->field[stoneTwo ] == field->squareIsFree) field->warnings[stoneTwo ] |= field->curPlayer->warning;
// stone was removed
} else if (field->field[stoneOne] == field->squareIsFree) {
// a warning is created
if (field->field[stoneTwo ] == field->curPlayer->id && field->field[stoneThree] == field->curPlayer->id) field->warnings[stoneOne] |= field->curPlayer->warning;
if (field->field[stoneTwo ] == field->oppPlayer->id && field->field[stoneThree] == field->oppPlayer->id) field->warnings[stoneOne] |= field->oppPlayer->warning;
// a warning is destroyed
if (field->warnings[stoneTwo] && field->field[stoneThree] != field->squareIsFree) {
// reset warning if necessary
if (field->field[field->neighbour[stoneTwo][0][0]] == field->curPlayer->id && field->field[field->neighbour[stoneTwo][0][1]] == field->curPlayer->id) field->warnings[stoneTwo ] = field->curPlayer->warning;
else if (field->field[field->neighbour[stoneTwo][1][0]] == field->curPlayer->id && field->field[field->neighbour[stoneTwo][1][1]] == field->curPlayer->id) field->warnings[stoneTwo ] = field->curPlayer->warning;
else if (field->field[field->neighbour[stoneTwo][0][0]] == field->oppPlayer->id && field->field[field->neighbour[stoneTwo][0][1]] == field->oppPlayer->id) field->warnings[stoneTwo ] = field->oppPlayer->warning;
else if (field->field[field->neighbour[stoneTwo][1][0]] == field->oppPlayer->id && field->field[field->neighbour[stoneTwo][1][1]] == field->oppPlayer->id) field->warnings[stoneTwo ] = field->oppPlayer->warning;
else field->warnings[stoneTwo ] = field->noWarning;
} else if (field->warnings[stoneThree] && field->field[stoneTwo ] != field->squareIsFree) {
// reset warning if necessary
if (field->field[field->neighbour[stoneThree][0][0]] == field->curPlayer->id && field->field[field->neighbour[stoneThree][0][1]] == field->curPlayer->id) field->warnings[stoneThree] = field->curPlayer->warning;
else if (field->field[field->neighbour[stoneThree][1][0]] == field->curPlayer->id && field->field[field->neighbour[stoneThree][1][1]] == field->curPlayer->id) field->warnings[stoneThree] = field->curPlayer->warning;
else if (field->field[field->neighbour[stoneThree][0][0]] == field->oppPlayer->id && field->field[field->neighbour[stoneThree][0][1]] == field->oppPlayer->id) field->warnings[stoneThree] = field->oppPlayer->warning;
else if (field->field[field->neighbour[stoneThree][1][0]] == field->oppPlayer->id && field->field[field->neighbour[stoneThree][1][1]] == field->oppPlayer->id) field->warnings[stoneThree] = field->oppPlayer->warning;
else field->warnings[stoneThree] = field->noWarning;
}
}
}
//-----------------------------------------------------------------------------
// Name: updateWarning()
// Desc:
//-----------------------------------------------------------------------------
inline void minMaxKI::updateWarning(unsigned int firstStone, unsigned int secondStone)
{
// set warnings
if (firstStone < field->size) setWarning(firstStone, field->neighbour[firstStone][0][0], field->neighbour[firstStone][0][1]);
if (firstStone < field->size) setWarning(firstStone, field->neighbour[firstStone][1][0], field->neighbour[firstStone][1][1]);
if (secondStone < field->size) setWarning(secondStone, field->neighbour[secondStone][0][0], field->neighbour[secondStone][0][1]);
if (secondStone < field->size) setWarning(secondStone, field->neighbour[secondStone][1][0], field->neighbour[secondStone][1][1]);
// no stone must be removed if each belongs to a mill
unsigned int i;
bool atLeastOneStoneRemoveAble = false;
if (field->stoneMustBeRemoved) for (i=0; i<field->size; i++) if (field->stonePartOfMill[i] == 0 && field->field[i] == field->oppPlayer->id) { atLeastOneStoneRemoveAble = true; break; }
if (!atLeastOneStoneRemoveAble) field->stoneMustBeRemoved = 0;
}
//-----------------------------------------------------------------------------
// Name: updatePossibleMoves()
// Desc:
//-----------------------------------------------------------------------------
inline void minMaxKI::updatePossibleMoves(unsigned int stone, playerStruct *stoneOwner, bool stoneRemoved, unsigned int ignoreStone)
{
// locals
unsigned int neighbor, direction;
// look into every direction
for (direction=0; direction<4; direction++) {
neighbor = field->connectedSquare[stone][direction];
// neighbor must exist
if (neighbor < field->size) {
// relevant when moving from one square to another connected square
if (ignoreStone == neighbor) continue;
// if there is no neighbour stone than it only affects the actual stone
if (field->field[neighbor] == field->squareIsFree) {
if (stoneRemoved) stoneOwner->numPossibleMoves--;
else stoneOwner->numPossibleMoves++;
// if there is a neighbour stone than it effects only this one
} else if (field->field[neighbor] == field->curPlayer->id) {
if (stoneRemoved) field->curPlayer->numPossibleMoves++;
else field->curPlayer->numPossibleMoves--;
} else {
if (stoneRemoved) field->oppPlayer->numPossibleMoves++;
else field->oppPlayer->numPossibleMoves--;
}
}}
// only 3 stones resting
if (field->curPlayer->numStones <= 3 && !field->settingPhase) field->curPlayer->numPossibleMoves = field->curPlayer->numStones * (field->size - field->curPlayer->numStones - field->oppPlayer->numStones);
if (field->oppPlayer->numStones <= 3 && !field->settingPhase) field->oppPlayer->numPossibleMoves = field->oppPlayer->numStones * (field->size - field->curPlayer->numStones - field->oppPlayer->numStones);
}
//-----------------------------------------------------------------------------
// Name: setStone()
// Desc:
//-----------------------------------------------------------------------------
inline void minMaxKI::setStone(unsigned int to, backupStruct *backup)
{
// backup
backup->from = field->size;
backup->to = to;
backup->fieldFrom = field->size;
backup->fieldTo = field->field[to];
// set stone into field
field->field[to] = field->curPlayer->id;
field->curPlayer->numStones++;
field->stonesSet++;
// setting phase finished ?
if (field->stonesSet == 18) field->settingPhase = false;
// update possible moves
updatePossibleMoves(to, field->curPlayer, false, field->size);
// update warnings
updateWarning(to, field->size);
}
//-----------------------------------------------------------------------------
// Name: normalMove()
// Desc:
//-----------------------------------------------------------------------------
inline void minMaxKI::normalMove(unsigned int from, unsigned int to, backupStruct *backup)
{
// backup
backup->from = from;
backup->to = to;
backup->fieldFrom = field->field[from];
backup->fieldTo = field->field[to ];
// set stone into field
field->field[from] = field->squareIsFree;
field->field[to] = field->curPlayer->id;
// update possible moves
updatePossibleMoves(from, field->curPlayer, true, to);
updatePossibleMoves(to, field->curPlayer, false, from);
// update warnings
updateWarning(from, to);
}
//-----------------------------------------------------------------------------
// Name: removeStone()
// Desc:
//-----------------------------------------------------------------------------
inline void minMaxKI::removeStone(unsigned int from, backupStruct *backup)
{
// backup
backup->from = from;
backup->to = field->size;
backup->fieldFrom = field->field[from];
backup->fieldTo = field->size;
// remove stone
field->field[from] = field->squareIsFree;
field->oppPlayer->numStones--;
field->oppPlayer->numStonesMissing++;
field->stoneMustBeRemoved--;
// update possible moves
updatePossibleMoves(from, field->oppPlayer, true, field->size);
// update warnings
updateWarning(from, field->size);
// end of game ?
if ((field->oppPlayer->numStones < 3) && (!field->settingPhase)) gameHasFinished = true;
}
//-----------------------------------------------------------------------------
// Name: move()
// Desc:
//-----------------------------------------------------------------------------
void minMaxKI::move(unsigned int threadNo, unsigned int idPossibility, bool opponentsMove, void **pBackup, void *pPossibilities)
{
// locals
backupStruct *oldState = &oldStates[curSearchDepth];
possibilityStruct *tmpPossibility = (possibilityStruct*) pPossibilities;
playerStruct *tmpPlayer;
unsigned int i;
// calculate place of stone
*pBackup = (void*) oldState;
oldState->value = currentValue;
oldState->gameHasFinished = gameHasFinished;
oldState->curPlayer = field->curPlayer;
oldState->oppPlayer = field->oppPlayer;
oldState->curNumStones = field->curPlayer->numStones;
oldState->oppNumStones = field->oppPlayer->numStones;
oldState->curPosMoves = field->curPlayer->numPossibleMoves;
oldState->oppPosMoves = field->oppPlayer->numPossibleMoves;
oldState->curMissStones = field->curPlayer->numStonesMissing;
oldState->oppMissStones = field->oppPlayer->numStonesMissing;
oldState->settingPhase = field->settingPhase;
oldState->stonesSet = field->stonesSet;
oldState->stoneMustBeRemoved= field->stoneMustBeRemoved;
curSearchDepth++;
// very expensive
for (i=0; i<field->size; i++) {
oldState->stonePartOfMill[i] = field->stonePartOfMill[i];
oldState->warnings[i] = field->warnings[i];
}
// move
if (field->stoneMustBeRemoved) { removeStone(idPossibility, oldState); }
else if (field->settingPhase) { setStone(idPossibility, oldState); }
else { normalMove(tmpPossibility->from[idPossibility], tmpPossibility->to[idPossibility], oldState); }
// when opponent is unable to move than current player has won
if ((!field->oppPlayer->numPossibleMoves) && (!field->settingPhase) && (!field->stoneMustBeRemoved) && (field->oppPlayer->numStones > 3)) gameHasFinished = true;
// calc value
if (!opponentsMove) currentValue = (float) field->oppPlayer->numStonesMissing - field->curPlayer->numStonesMissing + field->stoneMustBeRemoved + field->curPlayer->numPossibleMoves * 0.1f - field->oppPlayer->numPossibleMoves * 0.1f;
else currentValue = (float) field->curPlayer->numStonesMissing - field->oppPlayer->numStonesMissing - field->stoneMustBeRemoved + field->oppPlayer->numPossibleMoves * 0.1f - field->curPlayer->numPossibleMoves * 0.1f;
// when game has finished - perfect for the current player
if (gameHasFinished && !opponentsMove) currentValue = VALUE_GAME_WON - curSearchDepth;
if (gameHasFinished && opponentsMove) currentValue = VALUE_GAME_LOST + curSearchDepth;
// set next player
if (!field->stoneMustBeRemoved) {
tmpPlayer = field->curPlayer;
field->curPlayer = field->oppPlayer;
field->oppPlayer = tmpPlayer;
}
}
//-----------------------------------------------------------------------------
// Name: printMoveInformation()
// Desc:
//-----------------------------------------------------------------------------
void minMaxKI::printMoveInformation(unsigned int threadNo, unsigned int idPossibility, void *pPossibilities)
{
// locals
possibilityStruct *tmpPossibility = (possibilityStruct*) pPossibilities;
// move
if (field->stoneMustBeRemoved) cout << "remove stone from " << (char) (idPossibility + 97);
else if (field->settingPhase) cout << "set stone to " << (char) (idPossibility + 97);
else cout << "move from " << (char) (tmpPossibility->from[idPossibility] + 97) << " to " << (char) (tmpPossibility->to[idPossibility] + 97);
}

110
src/perfect/minMaxKI.h Normal file
View File

@ -0,0 +1,110 @@
/*********************************************************************\
minMaxKI.h
Copyright (c) Thomas Weber. All rights reserved.
Licensed under the MIT License.
https://github.com/madweasel/madweasels-cpp
\*********************************************************************/
#ifndef MINIMAXKI_H
#define MINIMAXKI_H
#include <iostream>
#include <cstdio>
#include <math.h>
#include "muehleKI.h"
#include "miniMax.h"
//using namespace std;
#define VALUE_GAME_LOST -1000.0f
#define VALUE_GAME_WON 1000.0f
/*** Klassen *********************************************************/
class minMaxKI : public muehleKI, miniMax
{
protected:
// structs
struct possibilityStruct
{
unsigned int from[MAX_NUM_POS_MOVES];
unsigned int to [MAX_NUM_POS_MOVES];
};
struct backupStruct
{
float value;
bool gameHasFinished;
bool settingPhase;
int fieldFrom, fieldTo; // value of field
unsigned int from, to; // index of field
unsigned int curNumStones, oppNumStones;
unsigned int curPosMoves, oppPosMoves;
unsigned int curMissStones, oppMissStones;
unsigned int stonesSet;
unsigned int stoneMustBeRemoved;
unsigned int stonePartOfMill[fieldStruct::size];
unsigned int warnings[fieldStruct::size];
playerStruct *curPlayer, *oppPlayer;
};
// Variables
fieldStruct *field; // pointer of the current field [changed by move()]
float currentValue; // value of current situation for field->currentPlayer
bool gameHasFinished; // someone has won or current field is full
int ownId; // id of the player who called the play()-function
unsigned int curSearchDepth; // current level
unsigned int depthOfFullTree; // search depth where the whole tree is explored
unsigned int *idPossibilities; // returned pointer of getPossibilities()-function
backupStruct *oldStates; // for undo()-function
possibilityStruct *possibilities; // for getPossNormalMove()-function
// Functions
unsigned int * getPossSettingPhase (unsigned int *numPossibilities, void **pPossibilities);
unsigned int * getPossNormalMove (unsigned int *numPossibilities, void **pPossibilities);
unsigned int * getPossStoneRemove (unsigned int *numPossibilities, void **pPossibilities);
// move functions
inline void updatePossibleMoves (unsigned int stone, playerStruct *stoneOwner, bool stoneRemoved, unsigned int ignoreStone);
inline void updateWarning (unsigned int firstStone, unsigned int secondStone);
inline void setWarning (unsigned int stoneOne, unsigned int stoneTwo, unsigned int stoneThree);
inline void removeStone (unsigned int from, backupStruct *backup);
inline void setStone (unsigned int to, backupStruct *backup);
inline void normalMove (unsigned int from, unsigned int to, backupStruct *backup);
// Virtual Functions
void prepareBestChoiceCalculation ();
unsigned int * getPossibilities (unsigned int threadNo, unsigned int *numPossibilities, bool *opponentsMove, void **pPossibilities);
void deletePossibilities (unsigned int threadNo, void *pPossibilities);
void move (unsigned int threadNo, unsigned int idPossibility, bool opponentsMove, void **pBackup, void *pPossibilities);
void undo (unsigned int threadNo, unsigned int idPossibility, bool opponentsMove, void *pBackup, void *pPossibilities);
void getValueOfSituation (unsigned int threadNo, float &floatValue, twoBit &shortValue);
void printMoveInformation (unsigned int threadNo, unsigned int idPossibility, void *pPossibilities);
unsigned int getNumberOfLayers () { return 0; };
unsigned int getNumberOfKnotsInLayer (unsigned int layerNum) { return 0; };
void getSuccLayers (unsigned int layerNum, unsigned int *amountOfSuccLayers, unsigned int *succLayers) { };
unsigned int getPartnerLayer (unsigned int layerNum) { return 0; };
string getOutputInformation (unsigned int layerNum) { return string("");};
void setOpponentLevel (unsigned int threadNo, bool isOpponentLevel) { };
bool setSituation (unsigned int threadNo, unsigned int layerNum, unsigned int stateNumber) { return false; };
bool getOpponentLevel (unsigned int threadNo) { return false; };
unsigned int getLayerAndStateNumber (unsigned int threadNo, unsigned int &layerNum, unsigned int &stateNumber) { return 0; };
unsigned int getLayerNumber (unsigned int threadNo) { return 0; };
void getSymStateNumWithDoubles (unsigned int threadNo, unsigned int *numSymmetricStates, unsigned int **symStateNumbers) { };
void getPredecessors (unsigned int threadNo, unsigned int *amountOfPred, retroAnalysisPredVars *predVars) { };
void printField (unsigned int threadNo, unsigned char value) { };
void prepareDatabaseCalculation () { };
void wrapUpDatabaseCalculation (bool calculationAborted) { };
public:
// Constructor / destructor
minMaxKI();
~minMaxKI();
// Functions
void play (fieldStruct *theField, unsigned int *pushFrom, unsigned int *pushTo);
void setSearchDepth (unsigned int depth);
};
#endif

289
src/perfect/miniMax.cpp Normal file
View File

@ -0,0 +1,289 @@
/*********************************************************************
miniMax.cpp
Copyright (c) Thomas Weber. All rights reserved.
Licensed under the MIT License.
https://github.com/madweasel/madweasels-cpp
\*********************************************************************/
#include "miniMax.h"
//-----------------------------------------------------------------------------
// Name: miniMax()
// Desc: miniMax class constructor
//-----------------------------------------------------------------------------
miniMax::miniMax()
{
// init default values
hFileShortKnotValues = NULL;
hFilePlyInfo = NULL;
memoryUsed2 = 0;
arrayInfos.c = this;
arrayInfos.arrayInfosToBeUpdated.clear();
arrayInfos.listArrays.clear();
onlyPrepareLayer = false;
curCalculatedLayer = 0;
osPrint = &cout;
verbosity = 3;
stopOnCriticalError = true;
pDataForUserPrintFunc = NULL;
userPrintFunc = NULL;
layerStats = NULL;
plyInfos = NULL;
fileDirectory.assign("");
InitializeCriticalSection(&csDatabase);
InitializeCriticalSection(&csOsPrint);
// Tausender-Trennzeichen
locale locale("German_Switzerland");
cout.imbue(locale);
// for io operations per second measurement
QueryPerformanceFrequency(&frequency);
numReadSkvOperations = 0;
numWriteSkvOperations = 0;
numReadPlyOperations = 0;
numWritePlyOperations = 0;
if (MEASURE_ONLY_IO) {
readSkvInterval.QuadPart = 0;
writeSkvInterval.QuadPart = 0;
readPlyInterval.QuadPart = 0;
writePlyInterval.QuadPart = 0;
} else {
QueryPerformanceCounter(&readSkvInterval );
QueryPerformanceCounter(&writeSkvInterval);
QueryPerformanceCounter(&readPlyInterval );
QueryPerformanceCounter(&writePlyInterval);
}
// The algorithm assumes that each player does only one move.
// That means closing a mill and removing a stone should be one move.
// PL_TO_MOVE_CHANGED means that in the predecessor state the player to move has changed to the other player.
// PL_TO_MOVE_UNCHANGED means that the player to move is still the one who shall move.
unsigned char skvPerspectiveMatrixTmp[4][2] = {
// PL_TO_MOVE_UNCHANGED PL_TO_MOVE_CHANGED
SKV_VALUE_INVALID, SKV_VALUE_INVALID, // SKV_VALUE_INVALID
SKV_VALUE_GAME_WON, SKV_VALUE_GAME_LOST, // SKV_VALUE_GAME_LOST
SKV_VALUE_GAME_DRAWN, SKV_VALUE_GAME_DRAWN, // SKV_VALUE_GAME_DRAWN
SKV_VALUE_GAME_LOST, SKV_VALUE_GAME_WON // SKV_VALUE_GAME_WON
};
memcpy(skvPerspectiveMatrix, skvPerspectiveMatrixTmp, 4 * 2);
}
//-----------------------------------------------------------------------------
// Name: ~miniMax()
// Desc: miniMax class destructor
//-----------------------------------------------------------------------------
miniMax::~miniMax()
{
closeDatabase();
DeleteCriticalSection(&csOsPrint);
DeleteCriticalSection(&csDatabase);
}
//-----------------------------------------------------------------------------
// Name: falseOrStop()
// Desc:
//-----------------------------------------------------------------------------
bool miniMax::falseOrStop()
{
if (stopOnCriticalError) WaitForSingleObject(GetCurrentProcess(), INFINITE);
return false;
}
//-----------------------------------------------------------------------------
// Name: getBestChoice()
// Desc: Returns the best choice if the database has been opened and calculates the best choice for that if database is not open.
//-----------------------------------------------------------------------------
void *miniMax::getBestChoice(unsigned int tilLevel, unsigned int *choice, unsigned int maximumNumberOfBranches)
{
// set global vars
depthOfFullTree = tilLevel;
maxNumBranches = maximumNumberOfBranches;
layerInDatabase = isCurrentStateInDatabase(0);
calcDatabase = false;
// Locals
knotStruct root;
alphaBetaGlobalVars alphaBetaVars(this, getLayerNumber(0));
runAlphaBetaVars tva(this, &alphaBetaVars, alphaBetaVars.layerNumber);
srand((unsigned int)time(NULL));
tva.curThreadNo = 0;
// prepare the situation
prepareBestChoiceCalculation();
// First make a tree until the desired level
letTheTreeGrow(&root, &tva, depthOfFullTree, FPKV_MIN_VALUE, FPKV_MAX_VALUE);
// pass best choice and close database
*choice = root.bestMoveId;
// Return the best branch of the root
return pRootPossibilities;
}
//-----------------------------------------------------------------------------
// Name: calculateDatabase()
// Desc: Calculates the database, which must be already open.
//-----------------------------------------------------------------------------
void miniMax::calculateDatabase(unsigned int maxDepthOfTree, bool onlyPrepareLayer)
{
// locals
bool abortCalculation = false;
this->onlyPrepareLayer = onlyPrepareLayer;
lastCalculatedLayer.clear();
PRINT(1, this, "*************************");
PRINT(1, this, "* Calculate Database *");
PRINT(1, this, "*************************");
// call preparation function of parent class
prepareDatabaseCalculation();
// when database not completed then do it
if (hFileShortKnotValues != NULL && skvfHeader.completed == false) {
// reserve memory
lastCalculatedLayer.clear();
depthOfFullTree = maxDepthOfTree;
layerInDatabase = false;
calcDatabase = true;
threadManager.uncancelExecution();
arrayInfos.vectorArrays.resize(arrayInfoStruct::numArrayTypes*skvfHeader.numLayers, arrayInfos.listArrays.end());
// calc layer after layer, beginning with the last one
for (curCalculatedLayer=0; curCalculatedLayer<skvfHeader.numLayers; curCalculatedLayer++) {
// layer already calculated?
if (layerStats[curCalculatedLayer].layerIsCompletedAndInFile) continue;
// don't calc if neither the layer nor the partner layer has any knots
if (layerStats[curCalculatedLayer].knotsInLayer == 0 && layerStats[layerStats[curCalculatedLayer].partnerLayer].knotsInLayer == 0) continue;
// calc
abortCalculation = (!calcLayer(curCalculatedLayer));
// relase memory
unloadAllLayers();
unloadAllPlyInfos();
// don't save layer and header when only preparing layers
if (onlyPrepareLayer) return;
if (abortCalculation) break;
// save header
saveHeader(&skvfHeader, layerStats);
saveHeader(&plyInfoHeader, plyInfos);
}
// don't save layer and header when only preparing layers or when
if (onlyPrepareLayer) return;
if (!abortCalculation) {
// calc layer statistics
calcLayerStatistics("statistics.txt");
// save header
skvfHeader.completed = true;
plyInfoHeader.plyInfoCompleted = true;
saveHeader(&skvfHeader, layerStats);
saveHeader(&plyInfoHeader, plyInfos);
}
// free mem
curCalculationActionId = MM_ACTION_NONE;
} else {
PRINT(1, this, "\nThe database is already fully calculated.\n");
}
// call warp-up function of parent class
wrapUpDatabaseCalculation(abortCalculation);
PRINT(1, this, "*************************");
PRINT(1, this, "* Calculation finished *");
PRINT(1, this, "*************************");
}
//-----------------------------------------------------------------------------
// Name: calcLayer()
// Desc:
//-----------------------------------------------------------------------------
bool miniMax::calcLayer(unsigned int layerNumber)
{
// locals
vector <unsigned int> layersToCalculate;
// moves can be done reverse, leading to too depth searching trees
if (shallRetroAnalysisBeUsed(layerNumber)) {
// calc values for all states of layer
layersToCalculate.push_back(layerNumber);
if (layerNumber != layerStats[layerNumber].partnerLayer) layersToCalculate.push_back(layerStats[layerNumber].partnerLayer);
if (!calcKnotValuesByRetroAnalysis(layersToCalculate)) return false;
// save partner layer
if (layerStats[layerNumber].partnerLayer != layerNumber) {
saveLayerToFile(layerStats[layerNumber].partnerLayer);
}
// use minimax-algorithm
} else {
if (!calcKnotValuesByAlphaBeta(layerNumber)) return false;
}
// save layer
saveLayerToFile(layerNumber);
// test layer
if (!testLayer(layerNumber)) {
PRINT(0, this, "ERROR: Layer calculation cancelled or failed!" << endl);
return false;
}
// test partner layer if retro-analysis has been used
if (shallRetroAnalysisBeUsed(layerNumber) && layerStats[layerNumber].partnerLayer != layerNumber) {
if (!testLayer(layerStats[layerNumber].partnerLayer)) {
PRINT(0, this, "ERROR: Layer calculation cancelled or failed!" << endl);
return false;
}
}
// update output information
EnterCriticalSection(&csOsPrint);
if (shallRetroAnalysisBeUsed(layerNumber) && layerNumber != layerStats[layerNumber].partnerLayer) {
lastCalculatedLayer.push_back(layerStats[layerNumber].partnerLayer);
}
lastCalculatedLayer.push_back(layerNumber);
LeaveCriticalSection(&csOsPrint);
return true;
}
//-----------------------------------------------------------------------------
// Name: pauseDatabaseCalculation()
// Desc:
//-----------------------------------------------------------------------------
void miniMax::pauseDatabaseCalculation()
{
threadManager.pauseExecution();
}
//-----------------------------------------------------------------------------
// Name: cancelDatabaseCalculation()
// Desc:
//-----------------------------------------------------------------------------
void miniMax::cancelDatabaseCalculation()
{
// when returning from executeParallelLoop() all function shall quit immediatelly up to calculateDatabase()
threadManager.cancelExecution();
}
//-----------------------------------------------------------------------------
// Name: wasDatabaseCalculationCancelled()
// Desc:
//-----------------------------------------------------------------------------
bool miniMax::wasDatabaseCalculationCancelled()
{
return threadManager.wasExecutionCancelled();
}

604
src/perfect/miniMax.h Normal file
View File

@ -0,0 +1,604 @@
/***************************************************************************************************************************
miniMax.h
Copyright (c) Thomas Weber. All rights reserved.
Licensed under the MIT License.
https://github.com/madweasel/madweasels-cpp
***************************************************************************************************************************/
#ifndef MINIMAX_H
#define MINIMAX_H
#include <windows.h>
#include <sstream>
#include <iostream>
#include <cstdio>
#include <list>
#include "Shlwapi.h"
#include <intrin.h>
#include <time.h>
#include <vector>
#include <algorithm>
#include "cyclicArray.h"
#include "strLib.h"
#include "threadManager.h"
#include "bufferedFile.h"
#pragma intrinsic(_rotl8, _rotr8) // for shifting bits
using namespace std; // use standard library namespace
/*** Wiki ***************************************************************************************************************************
player:
layer: The states are divided in layers. For example depending on number of stones on the field.
state: A unique game state reprensiting a current game situation.
situation: Used as synonym to state.
knot: Each knot of the graph corresponds to a game state. The knots are connected by possible valid moves.
ply info: Number of plies/moves necessary to win the game.
state adress: A state is identified by the corresponding layer and the state number within the layer.
short knot value: Each knot/state can have the value SKV_VALUE_INVALID, SKV_VALUE_GAME_LOST, SKV_VALUE_GAME_DRAWN or SKV_VALUE_GAME_WON.
float point knot value: Each knot/state can be evaluated by a floating point value. High positive values represents winning situations. Negative values stand for loosing situations.
database: The database contains the arrays with the short knot values and the ply infos.
/*** Constants ***************************************************************************************************************************/
#define FPKV_MIN_VALUE -100000.0f // minimum float point knot value
#define FPKV_MAX_VALUE 100000.0f // maximum float point knot value
#define FPKV_THRESHOLD 0.001f // threshold used when choosing best move. knot values differing less than this threshold will be regarded as egal
#define SKV_VALUE_INVALID 0 // short knot value: knot value is invalid
#define SKV_VALUE_GAME_LOST 1 // game lost means that there is no perfect move possible
#define SKV_VALUE_GAME_DRAWN 2 // the perfect move leads at least to a drawn game
#define SKV_VALUE_GAME_WON 3 // the perfect move will lead to a won game
#define SKV_MAX_VALUE 3 // highest short knot value
#define SKV_NUM_VALUES 4 // number of different short knot values
#define SKV_WHOLE_BYTE_IS_INVALID 0 // four short knot values are stored in one byte. so all four knot values are invalid
#define PLYINFO_EXP_VALUE 1000 // expected maximum number of plies -> user for vector initialization
#define PLYINFO_VALUE_DRAWN 65001 // knot value is drawn. since drawn means a never ending game, this is a special ply info
#define PLYINFO_VALUE_UNCALCULATED 65002 // ply info is not calculated yet for this game state
#define PLYINFO_VALUE_INVALID 65003 // ply info is invalid, since knot value is invalid
#define MAX_NUM_PRED_LAYERS 2 // each layer must have at maximum two preceding layers
#define SKV_FILE_HEADER_CODE 0xF4F5 // constant to identify the header
#define PLYINFO_HEADER_CODE 0xF3F2 // ''
#define OUTPUT_EVERY_N_STATES 10000000 // print progress every n-th processed knot
#define BLOCK_SIZE_IN_CYCLIC_ARRAY 10000 // BLOCK_SIZE_IN_CYCLIC_ARRAY*sizeof(stateAdressStruct) = block size in bytes for the cyclic arrays
#define MAX_NUM_PREDECESSORS 10000 // maximum number of predecessors. important for array sizes
#define FILE_BUFFER_SIZE 1000000 // size in bytes
#define PL_TO_MOVE_CHANGED 1 // player to move changed - second index of the 2D-array skvPerspectiveMatrix[][]
#define PL_TO_MOVE_UNCHANGED 0 // player to move is still the same - second index of the 2D-array skvPerspectiveMatrix[][]
#define MEASURE_TIME_FREQUENCY 100000 // for io operations per second: measure time every n-th operations
#define MEASURE_IOPS false // true or false - for measurement of the input/output operations per second
#define MEASURE_ONLY_IO false // true or false - to indicate if only the io-operation shall be considered or also the calculating time inbetween
#define MM_ACTION_INIT_RETRO_ANAL 1
#define MM_ACTION_PREPARE_COUNT_ARRAY 2
#define MM_ACTION_PERFORM_RETRO_ANAL 3
#define MM_ACTION_PERFORM_ALPHA_BETA 4
#define MM_ACTION_TESTING_LAYER 5
#define MM_ACTION_SAVING_LAYER_TO_FILE 6
#define MM_ACTION_CALC_LAYER_STATS 7
#define MM_ACTION_NONE 8
/*** Macros ***************************************************************************************************************************/
#define SAFE_DELETE(p) { if(p) { delete (p); (p)=NULL; } }
#define SAFE_DELETE_ARRAY(p) { if(p) { delete[] (p); (p)=NULL; } }
// here a macro is used instead of a function because the text 't' is passed like "blabla" << endl << aVariable
#define PRINT(v, c, t) \
{ \
if (c->verbosity > v) { \
EnterCriticalSection(&c->csOsPrint); \
*c->osPrint << endl << t; \
if (c->userPrintFunc != NULL) { \
c->userPrintFunc(c->pDataForUserPrintFunc); \
} \
LeaveCriticalSection(&c->csOsPrint); \
} \
}
/*** Klassen ***************************************************************************************************************************/
class miniMax
{
friend class miniMaxWinInspectDb;
friend class miniMaxWinCalcDb;
public:
/*** typedefines ***************************************************************************************************************************/
typedef unsigned char twoBit; // 2-Bit variable ranging from 0 to 3
typedef unsigned short plyInfoVarType; // 2 Bytes for saving the ply info
typedef unsigned char countArrayVarType; // 1 Byte for counting predesseccors
typedef unsigned int stateNumberVarType; // 4 Bytes for addressing states within a layer
/*** protected structures ********************************************************************************************************************/
struct skvFileHeaderStruct // header of the short knot value file
{
bool completed; // true if all states have been calculated
unsigned int numLayers; // number of layers
unsigned int headerCode; // = SKV_FILE_HEADER_CODE
unsigned int headerAndStatsSize; // size in bytes of this struct plus the stats
};
struct plyInfoFileHeaderStruct
{
bool plyInfoCompleted; // true if ply innformation has been calculated for all game states
unsigned int numLayers; // number of layers
unsigned int headerCode; // = PLYINFO_HEADER_CODE
unsigned int headerAndPlyInfosSize; // size in bytes of this struct plus ...
};
struct plyInfoStruct // this struct is created for each layer
{
bool plyInfoIsLoaded; // the array plyInfo[] exists in memory. does not necessary mean that it contains only valid values
bool plyInfoIsCompletedAndInFile; // the array plyInfo[] contains only fully calculated valid values
long long layerOffset; // position of this struct in the ply info file
unsigned int sizeInBytes; // size of this struct plus the array plyInfo[]
stateNumberVarType knotsInLayer; // number of knots of the corresponding layer
plyInfoVarType * plyInfo; // array of size [knotsInLayer] containing the ply info for each knot in this layer
// compressorClass::compressedArrayClass * plyInfoCompressed; // compressed array containing the ply info for each knot in this layer
void* plyInfoCompressed; // dummy pointer for padding
};
struct layerStatsStruct
{
bool layerIsLoaded; // the array shortKnotValueByte[] exists in memory. does not necessary mean that it contains only valid values
bool layerIsCompletedAndInFile; // the array shortKnotValueByte[] contains only fully calculated valid values
long long layerOffset; // position of this struct in the short knot value file
unsigned int numSuccLayers; // number of succeding layers. states of other layers are connected by a move of a player
unsigned int succLayers[MAX_NUM_PRED_LAYERS];// array containg the layer ids of the succeding layers
unsigned int partnerLayer; // layer id relevant when switching current and opponent player
stateNumberVarType knotsInLayer; // number of knots of the corresponding layer
stateNumberVarType numWonStates; // number of won states in this layer
stateNumberVarType numLostStates; // number of lost states in this layer
stateNumberVarType numDrawnStates; // number of drawn states in this layer
stateNumberVarType numInvalidStates; // number of invalid states in this layer
unsigned int sizeInBytes; // (knotsInLayer + 3) / 4
twoBit * shortKnotValueByte; // array of size [sizeInBytes] containg the short knot values
//compressorClass::compressedArrayClass * skvCompressed; // compressed array containing the short knot values
void* skvCompressed; // dummy pointer for padding
};
struct stateAdressStruct
{
stateNumberVarType stateNumber; // state id within the corresponding layer
unsigned char layerNumber; // layer id
};
struct knotStruct
{
bool isOpponentLevel; // the current considered knot belongs to an opponent game state
float floatValue; // Value of knot (for normal mode)
twoBit shortValue; // Value of knot (for database)
unsigned int bestMoveId; // for calling class
unsigned int bestBranch; // branch with highest value
unsigned int numPossibilities; // number of branches
plyInfoVarType plyInfo; // number of moves till win/lost
knotStruct * branches; // pointer to branches
};
struct retroAnalysisPredVars
{
unsigned int predStateNumbers; //
unsigned int predLayerNumbers; //
unsigned int predSymOperation; //
bool playerToMoveChanged; //
};
struct arrayInfoStruct
{
unsigned int type; //
long long sizeInBytes; //
long long compressedSizeInBytes; //
unsigned int belongsToLayer; //
unsigned int updateCounter;
static const unsigned int arrayType_invalid = 0;
static const unsigned int arrayType_knotAlreadyCalculated = 1;
static const unsigned int arrayType_countArray = 2;
static const unsigned int arrayType_plyInfos = 3;
static const unsigned int arrayType_layerStats = 4;
static const unsigned int numArrayTypes = 5;
static const unsigned int updateCounterThreshold = 100;
};
struct arrayInfoChange
{
unsigned int itemIndex; //
arrayInfoStruct * arrayInfo; //
};
struct arrayInfoContainer
{
miniMax* c;
list<arrayInfoChange> arrayInfosToBeUpdated; //
list<arrayInfoStruct> listArrays; // [itemIndex]
vector<list<arrayInfoStruct>::iterator> vectorArrays; // [layerNumber*arrayInfoStruct::numArrayTypes + type]
void addArray (unsigned int layerNumber, unsigned int type, long long size, long long compressedSize);
void removeArray (unsigned int layerNumber, unsigned int type, long long size, long long compressedSize);
void updateArray (unsigned int layerNumber, unsigned int type);
};
/*** public functions ***************************************************************************************************************************/
// Constructor / destructor
miniMax();
~miniMax();
// Testing functions
bool testState (unsigned int layerNumber, unsigned int stateNumber);
bool testLayer (unsigned int layerNumber);
bool testIfSymStatesHaveSameValue (unsigned int layerNumber);
bool testSetSituationAndGetPoss (unsigned int layerNumber);
// Statistics
bool calcLayerStatistics (char *statisticsFileName);
void showMemoryStatus ();
unsigned int getNumThreads ();
bool anyFreshlyCalculatedLayer ();
unsigned int getLastCalculatedLayer ();
stateNumberVarType getNumWonStates (unsigned int layerNum);
stateNumberVarType getNumLostStates (unsigned int layerNum);
stateNumberVarType getNumDrawnStates (unsigned int layerNum);
stateNumberVarType getNumInvalidStates (unsigned int layerNum);
bool isLayerInDatabase (unsigned int layerNum);
long long getLayerSizeInBytes (unsigned int layerNum);
void setOutputStream (ostream * theStream, void(*printFunc)(void *pUserData), void *pUserData);
bool anyArrawInfoToUpdate ();
arrayInfoChange getArrayInfoForUpdate ();
void getCurrentCalculatedLayer (vector<unsigned int> &layers);
LPWSTR getCurrentActionStr ();
// Main function for getting the best choice
void * getBestChoice (unsigned int tilLevel, unsigned int *choice, unsigned int maximumNumberOfBranches);
// Database functions
bool openDatabase (const char *directory, unsigned int maximumNumberOfBranches);
void calculateDatabase (unsigned int maxDepthOfTree, bool onlyPrepareLayer);
bool isCurrentStateInDatabase (unsigned int threadNo);
void closeDatabase ();
void unloadAllLayers ();
void unloadAllPlyInfos ();
void pauseDatabaseCalculation ();
void cancelDatabaseCalculation ();
bool wasDatabaseCalculationCancelled ();
// Virtual Functions
virtual void prepareBestChoiceCalculation () { while (true); }; // is called once before building the tree
virtual unsigned int * getPossibilities (unsigned int threadNo, unsigned int *numPossibilities, bool *opponentsMove, void **pPossibilities) { while (true); return 0; }; // returns a pointer to the possibility-IDs
virtual void deletePossibilities (unsigned int threadNo, void *pPossibilities) { while (true); };
virtual void storeValueOfMove (unsigned int threadNo, unsigned int idPossibility, void *pPossibilities, twoBit value, unsigned int *freqValuesSubMoves, plyInfoVarType plyInfo) {};
virtual void move (unsigned int threadNo, unsigned int idPossibility, bool opponentsMove, void **pBackup, void *pPossibilities) { while (true); };
virtual void undo (unsigned int threadNo, unsigned int idPossibility, bool opponentsMove, void *pBackup, void *pPossibilities) { while (true); };
virtual bool shallRetroAnalysisBeUsed (unsigned int layerNum) { return false; };
virtual unsigned int getNumberOfLayers () { while (true); return 0; };
virtual unsigned int getNumberOfKnotsInLayer (unsigned int layerNum) { while (true); return 0; };
virtual void getSuccLayers (unsigned int layerNum, unsigned int *amountOfSuccLayers, unsigned int *succLayers) { while (true); };
virtual unsigned int getPartnerLayer (unsigned int layerNum) { while (true); return 0; };
virtual string getOutputInformation (unsigned int layerNum) { while (true); return string(""); };
virtual void setOpponentLevel (unsigned int threadNo, bool isOpponentLevel) { while (true); };
virtual bool setSituation (unsigned int threadNo, unsigned int layerNum, unsigned int stateNumber) { while (true); return false; };
virtual void getValueOfSituation (unsigned int threadNo, float &floatValue, twoBit &shortValue) { while (true); }; // value of situation for the initial current player
virtual bool getOpponentLevel (unsigned int threadNo) { while (true); return false; };
virtual unsigned int getLayerAndStateNumber (unsigned int threadNo, unsigned int &layerNum, unsigned int &stateNumber) { while (true); return 0; };
virtual unsigned int getLayerNumber (unsigned int threadNo) { while (true); return 0; };
virtual void getSymStateNumWithDoubles (unsigned int threadNo, unsigned int *numSymmetricStates, unsigned int **symStateNumbers) { while (true); };
virtual void getPredecessors (unsigned int threadNo, unsigned int *amountOfPred, retroAnalysisPredVars *predVars) { while (true); };
virtual void printField (unsigned int threadNo, unsigned char value) { while (true); };
virtual void printMoveInformation (unsigned int threadNo, unsigned int idPossibility, void *pPossibilities) { while (true); };
virtual void prepareDatabaseCalculation () { while (true); };
virtual void wrapUpDatabaseCalculation (bool calculationAborted) { while (true); };
private:
/*** classes for testing ********************************************************************************************************************/
struct testLayersVars {
miniMax * pMiniMax;
unsigned int curThreadNo;
unsigned int layerNumber;
LONGLONG statesProcessed;
twoBit * subValueInDatabase;
plyInfoVarType * subPlyInfos;
bool * hasCurPlayerChanged;
};
/*** classes for the alpha beta algorithmn ********************************************************************************************************************/
struct alphaBetaThreadVars // thread specific variables for each thread in the alpha beta algorithmn
{
long long numStatesToProcess; // Number of states in 'statesToProcess' which have to be processed
unsigned int threadNo;
};
struct alphaBetaGlobalVars // constant during calculation
{
unsigned int layerNumber; // layer number of the current process layer
long long totalNumKnots; // total numbers of knots which have to be stored in memory
long long numKnotsToCalc; // number of knots of all layers to be calculated
vector<alphaBetaThreadVars> thread;
unsigned int statsValueCounter[SKV_NUM_VALUES];
miniMax * pMiniMax;
alphaBetaGlobalVars(miniMax *pMiniMax, unsigned int layerNumber)
{
this->thread.resize(pMiniMax->threadManager.getNumThreads());
for (unsigned int threadNo=0; threadNo<pMiniMax->threadManager.getNumThreads(); threadNo++) {
this->thread[threadNo].numStatesToProcess = 0;
this->thread[threadNo].threadNo = threadNo;
}
this->layerNumber = layerNumber;
this->pMiniMax = pMiniMax;
if (pMiniMax->layerStats) {
this->numKnotsToCalc = pMiniMax->layerStats[layerNumber].knotsInLayer;
this->totalNumKnots = pMiniMax->layerStats[layerNumber].knotsInLayer;
}
this->statsValueCounter[SKV_VALUE_GAME_WON ] = 0;
this->statsValueCounter[SKV_VALUE_GAME_LOST ] = 0;
this->statsValueCounter[SKV_VALUE_GAME_DRAWN] = 0;
this->statsValueCounter[SKV_VALUE_INVALID ] = 0;
}
};
struct alphaBetaDefaultThreadVars
{
miniMax * pMiniMax;
alphaBetaGlobalVars * alphaBetaVars;
unsigned int layerNumber;
LONGLONG statesProcessed;
unsigned int statsValueCounter[SKV_NUM_VALUES];
alphaBetaDefaultThreadVars() {};
alphaBetaDefaultThreadVars(miniMax *pMiniMax, alphaBetaGlobalVars * alphaBetaVars, unsigned int layerNumber)
{
this->statesProcessed = 0;
this->layerNumber = layerNumber;
this->pMiniMax = pMiniMax;
this->alphaBetaVars = alphaBetaVars;
for (unsigned int curStateValue=0; curStateValue<SKV_NUM_VALUES; curStateValue++) {
this->statsValueCounter[curStateValue] = 0;
}
};
void reduceDefault ()
{
pMiniMax->numStatesProcessed += this->statesProcessed;
for (unsigned int curStateValue=0; curStateValue<SKV_NUM_VALUES; curStateValue++) {
alphaBetaVars->statsValueCounter[curStateValue] += this->statsValueCounter[curStateValue];
}
};
};
struct initAlphaBetaVars : public threadManagerClass::threadVarsArrayItem, public alphaBetaDefaultThreadVars
{
bufferedFileClass * bufferedFile;
bool initAlreadyDone;
initAlphaBetaVars() {};
initAlphaBetaVars(miniMax *pMiniMax, alphaBetaGlobalVars * alphaBetaVars, unsigned int layerNumber, bufferedFileClass * initArray, bool initAlreadyDone) : alphaBetaDefaultThreadVars(pMiniMax, alphaBetaVars, layerNumber)
{
this->bufferedFile = initArray;
this->initAlreadyDone = initAlreadyDone;
};
void initializeElement (initAlphaBetaVars &master) { *this = master; };
void reduce () { reduceDefault(); };
};
struct runAlphaBetaVars : public threadManagerClass::threadVarsArrayItem, public alphaBetaDefaultThreadVars
{
knotStruct * branchArray = NULL; // array of size [(depthOfFullTree - tilLevel) * maxNumBranches] for storage of the branches at each search depth
unsigned int * freqValuesSubMovesBranchWon = NULL; // ...
unsigned int freqValuesSubMoves[4]; // ...
runAlphaBetaVars () {};
runAlphaBetaVars (miniMax* pMiniMax, alphaBetaGlobalVars* alphaBetaVars, unsigned int layerNumber) : alphaBetaDefaultThreadVars(pMiniMax, alphaBetaVars, layerNumber) { initializeElement(*this); };
~runAlphaBetaVars () { SAFE_DELETE_ARRAY(branchArray); SAFE_DELETE_ARRAY(freqValuesSubMovesBranchWon); }
void reduce () { reduceDefault(); };
void initializeElement (runAlphaBetaVars &master)
{
*this = master;
branchArray = new knotStruct [alphaBetaVars->pMiniMax->maxNumBranches * alphaBetaVars->pMiniMax->depthOfFullTree];
freqValuesSubMovesBranchWon = new unsigned int[alphaBetaVars->pMiniMax->maxNumBranches];
};
};
/*** classes for the retro analysis ***************************************************************************************************************************/
struct retroAnalysisQueueState
{
stateNumberVarType stateNumber; // state stored in the retro analysis queue. the queue is a buffer containing states to be passed to 'retroAnalysisThreadVars::statesToProcess'
plyInfoVarType numPliesTillCurState; // ply number for the stored state
};
struct retroAnalysisThreadVars // thread specific variables for each thread in the retro analysis
{
vector<cyclicArray*> statesToProcess; // vector-queue containing the states, whose short knot value are known for sure. they have to be processed. if processed the state will be removed from list. indexing: [threadNo][plyNumber]
vector<vector<retroAnalysisQueueState>> stateQueue; // Queue containing states, whose 'count value' shall be increased by one. Before writing 'count value' to 'count array' the writing positions are sorted for faster processing.
long long numStatesToProcess; // Number of states in 'statesToProcess' which have to be processed
unsigned int threadNo;
};
struct retroAnalysisGlobalVars // constant during calculation
{
vector<countArrayVarType *> countArrays; // One count array for each layer in 'layersToCalculate'. (For the nine men's morris game two layers have to considered at once.)
vector<bool> layerInitialized; //
vector<unsigned int> layersToCalculate; // layers which shall be calculated
long long totalNumKnots; // total numbers of knots which have to be stored in memory
long long numKnotsToCalc; // number of knots of all layers to be calculated
vector<retroAnalysisThreadVars> thread;
unsigned int statsValueCounter[SKV_NUM_VALUES];
miniMax * pMiniMax;
};
struct retroAnalysisDefaultThreadVars
{
miniMax * pMiniMax;
retroAnalysisGlobalVars * retroVars;
unsigned int layerNumber;
LONGLONG statesProcessed;
unsigned int statsValueCounter[SKV_NUM_VALUES];
retroAnalysisDefaultThreadVars() {};
retroAnalysisDefaultThreadVars(miniMax *pMiniMax, retroAnalysisGlobalVars * retroVars, unsigned int layerNumber)
{
this->statesProcessed = 0;
this->layerNumber = layerNumber;
this->pMiniMax = pMiniMax;
this->retroVars = retroVars;
for (unsigned int curStateValue=0; curStateValue<SKV_NUM_VALUES; curStateValue++) {
this->statsValueCounter[curStateValue] = 0;
}
};
void reduceDefault ()
{
pMiniMax->numStatesProcessed += this->statesProcessed;
for (unsigned int curStateValue=0; curStateValue<SKV_NUM_VALUES; curStateValue++) {
retroVars->statsValueCounter[curStateValue] += this->statsValueCounter[curStateValue];
}
};
};
struct initRetroAnalysisVars : public threadManagerClass::threadVarsArrayItem, public retroAnalysisDefaultThreadVars
{
bufferedFileClass * bufferedFile;
bool initAlreadyDone;
initRetroAnalysisVars() {};
initRetroAnalysisVars(miniMax *pMiniMax, retroAnalysisGlobalVars * retroVars, unsigned int layerNumber, bufferedFileClass * initArray, bool initAlreadyDone) : retroAnalysisDefaultThreadVars(pMiniMax, retroVars, layerNumber)
{
this->bufferedFile = initArray;
this->initAlreadyDone = initAlreadyDone;
};
void initializeElement (initRetroAnalysisVars &master) { *this = master; };
void reduce () { reduceDefault(); };
};
struct addNumSuccedorsVars : public threadManagerClass::threadVarsArrayItem, public retroAnalysisDefaultThreadVars
{
retroAnalysisPredVars predVars[MAX_NUM_PREDECESSORS];
addNumSuccedorsVars() {};
addNumSuccedorsVars(miniMax *pMiniMax, retroAnalysisGlobalVars * retroVars, unsigned int layerNumber) : retroAnalysisDefaultThreadVars(pMiniMax, retroVars, layerNumber)
{
};
void initializeElement (addNumSuccedorsVars &master) { *this = master; };
void reduce () { reduceDefault(); };
};
/*** private variables ***************************************************************************************************************************/
// variables, which are constant during database calculation
int verbosity = 2; // output detail level. default is 2
unsigned char skvPerspectiveMatrix[4][2]; // [short knot value][current or opponent player] - A winning situation is a loosing situation for the opponent and so on ...
bool calcDatabase = false; // true, if the database is currently beeing calculated
HANDLE hFileShortKnotValues = NULL; // handle of the file for the short knot value
HANDLE hFilePlyInfo = NULL; // handle of the file for the ply info
skvFileHeaderStruct skvfHeader; // short knot value file header
plyInfoFileHeaderStruct plyInfoHeader; // header of the ply info file
string fileDirectory; // path of the folder where the database files are located
ostream * osPrint = NULL; // stream for output. default is cout
list<unsigned int> lastCalculatedLayer; //
vector<unsigned int> layersToCalculate; // used in calcLayer() and getCurrentCalculatedLayers()
bool onlyPrepareLayer = false; //
bool stopOnCriticalError = true; // if true then process will stay in while loop
threadManagerClass threadManager; //
CRITICAL_SECTION csDatabase; //
CRITICAL_SECTION csOsPrint; // for thread safety when output is passed to osPrint
void (*userPrintFunc)(void *) = NULL; // called every time output is passed to osPrint
void * pDataForUserPrintFunc = NULL; // pointer passed when calling userPrintFunc
arrayInfoContainer arrayInfos; // information about the arrays in memory
// thread specific or non-constant variables
LONGLONG memoryUsed2 = 0; // memory in bytes used for storing: ply information, short knot value and ...
LONGLONG numStatesProcessed = 0; //
unsigned int maxNumBranches = 0; // maximum number of branches/moves
unsigned int depthOfFullTree = 0; // maxumim search depth
unsigned int curCalculatedLayer = 0; // id of the currently calculated layer
unsigned int curCalculationActionId = 0; // one of ...
bool layerInDatabase = false; // true if the current considered layer has already been calculated and stored in the database
void * pRootPossibilities = NULL; // pointer to the structure passed by getPossibilities() for the state at which getBestChoice() has been called
layerStatsStruct * layerStats = NULL; // array of size [] containing general layer information and the skv of all layers
plyInfoStruct * plyInfos = NULL; // array of size [] containing ply information
// variables concerning the compression of the database
// compressorClass * compressor = NULL;
// unsigned int compressionAlgorithmnId = 0; // 0 or one of the COMPRESSOR_ALG_... constants
// database io operations per second
long long numReadSkvOperations = 0; // number of read operations done since start of the programm
long long numWriteSkvOperations = 0; // number of write operations done since start of the programm
long long numReadPlyOperations = 0; // number of read operations done since start of the programm
long long numWritePlyOperations = 0; // number of write operations done since start of the programm
LARGE_INTEGER readSkvInterval; // time of interval for read operations
LARGE_INTEGER writeSkvInterval; // ''
LARGE_INTEGER readPlyInterval; // ''
LARGE_INTEGER writePlyInterval; // ''
LARGE_INTEGER frequency; // performance-counter frequency, in counts per second
/*** private functions ***************************************************************************************************************************/
// database functions
void openSkvFile (const char *path, unsigned int maximumNumberOfBranches);
void openPlyInfoFile (const char *path);
bool calcLayer (unsigned int layerNumber);
void unloadPlyInfo (unsigned int layerNumber);
void unloadLayer (unsigned int layerNumber);
void saveHeader (skvFileHeaderStruct *dbH, layerStatsStruct *lStats);
void saveHeader (plyInfoFileHeaderStruct *piH, plyInfoStruct *pInfo);
void readKnotValueFromDatabase (unsigned int threadNo, unsigned int &layerNumber, unsigned int &stateNumber, twoBit &knotValue, bool &invalidLayerOrStateNumber, bool &layerInDatabaseAndCompleted);
void readKnotValueFromDatabase (unsigned int layerNumber, unsigned int stateNumber, twoBit &knotValue);
void readPlyInfoFromDatabase (unsigned int layerNumber, unsigned int stateNumber, plyInfoVarType &value);
void saveKnotValueInDatabase (unsigned int layerNumber, unsigned int stateNumber, twoBit knotValue);
void savePlyInfoInDatabase (unsigned int layerNumber, unsigned int stateNumber, plyInfoVarType value);
void loadBytesFromFile (HANDLE hFile, long long offset, unsigned int numBytes, void *pBytes);
void saveBytesToFile (HANDLE hFile, long long offset, unsigned int numBytes, void *pBytes);
void saveLayerToFile (unsigned int layerNumber);
inline void measureIops (long long &numOperations, LARGE_INTEGER &interval, LARGE_INTEGER &curTimeBefore, char text[]);
// Testing functions
static DWORD testLayerThreadProc (void* pParameter, int index);
static DWORD testSetSituationThreadProc (void* pParameter, int index);
// Alpha-Beta-Algorithmn
bool calcKnotValuesByAlphaBeta (unsigned int layerNumber);
bool initAlphaBeta (alphaBetaGlobalVars &retroVars);
bool runAlphaBeta (alphaBetaGlobalVars &retroVars);
void letTheTreeGrow (knotStruct *knot, runAlphaBetaVars *rabVars, unsigned int tilLevel, float alpha, float beta);
bool alphaBetaTryDataBase (knotStruct *knot, runAlphaBetaVars *rabVars, unsigned int tilLevel, unsigned int &layerNumber, unsigned int &stateNumber);
void alphaBetaTryPossibilites (knotStruct *knot, runAlphaBetaVars *rabVars, unsigned int tilLevel, unsigned int *idPossibility, void *pPossibilities, unsigned int &maxWonfreqValuesSubMoves, float &alpha, float &beta);
void alphaBetaCalcPlyInfo (knotStruct *knot);
void alphaBetaCalcKnotValue (knotStruct *knot);
void alphaBetaChooseBestMove (knotStruct *knot, runAlphaBetaVars *rabVars, unsigned int tilLevel, unsigned int *idPossibility, unsigned int maxWonfreqValuesSubMoves);
void alphaBetaSaveInDatabase (unsigned int threadNo, unsigned int layerNumber, unsigned int stateNumber, twoBit knotValue, plyInfoVarType plyValue, bool invertValue);
static DWORD initAlphaBetaThreadProc (void* pParameter, int index);
static DWORD runAlphaBetaThreadProc (void* pParameter, int index);
// Retro Analysis
bool calcKnotValuesByRetroAnalysis (vector<unsigned int> &layersToCalculate);
bool initRetroAnalysis (retroAnalysisGlobalVars &retroVars);
bool prepareCountArrays (retroAnalysisGlobalVars &retroVars);
bool calcNumSuccedors (retroAnalysisGlobalVars &retroVars);
bool performRetroAnalysis (retroAnalysisGlobalVars &retroVars);
bool addStateToProcessQueue (retroAnalysisGlobalVars &retroVars, retroAnalysisThreadVars &threadVars, unsigned int plyNumber, stateAdressStruct* pState);
static bool retroAnalysisQueueStateComp (const retroAnalysisQueueState &a, const retroAnalysisQueueState &b) {return a.stateNumber < b.stateNumber; };
static DWORD initRetroAnalysisThreadProc (void* pParameter, int index);
static DWORD addNumSuccedorsThreadProc (void* pParameter, int index);
static DWORD performRetroAnalysisThreadProc (void* pParameter);
// Progress report functions
void showLayerStats (unsigned int layerNumber);
bool falseOrStop ();
};
#endif

158
src/perfect/miniMaxWin.h Normal file
View File

@ -0,0 +1,158 @@
/*********************************************************************
miniMaxWin.h
Copyright (c) Thomas Weber. All rights reserved.
Licensed under the MIT License.
https://github.com/madweasel/madweasels-cpp
\*********************************************************************/
#ifndef MINIMAXWIN_H
#define MINIMAXWIN_H
// Windows Header Files:
#include "miniMax\\miniMax.h"
#include <queue>
#include <thread>
#include <mutex>
#include <condition_variable>
class miniMaxGuiField
{
public:
virtual void setAlignment (wildWeasel::alignment& newAlignment) {};
virtual void setVisibility (bool visible) {};
virtual void setState (unsigned int curShowedLayer, miniMax::stateNumberVarType curShowedState) {};
};
/*------------------------------------------------------------------------------------
| ------------------------------------- --------------------------------- |
| | | | | |
| | | | | |
| | pTreeViewInspect | | miniMaxGuiField | |
| | | | | |
| | | | | |
| | | | | |
| | | | | |
| | | | | |
| | | | | |
| | | | | |
| | | | | |
| | | | | |
| | | | | |
| | | | | |
| | | | | |
| | | | | |
| | | | | |
| | | | | |
| ------------------------------------- --------------------------------- |
| |
-----------------------------------------------------------------------------------*/
class miniMaxWinInspectDb
{
protected:
// General Variables
miniMax * pMiniMax = nullptr; // pointer to perfect KI class granting the access to the database
miniMaxGuiField* pGuiField = nullptr;
bool showingInspectionControls = false;
unsigned int curShowedLayer = 0; // current showed layer
miniMax::stateNumberVarType curShowedState = 0; // current showed state
const unsigned int scrollBarWidth = 20;
public:
// Constructor / destructor
miniMaxWinInspectDb (wildWeasel::masterMind* ww, miniMax* pMiniMax, wildWeasel::alignment& amInspectDb, wildWeasel::font2D* font, wildWeasel::texture* textureLine, miniMaxGuiField& guiField);
~miniMaxWinInspectDb ();
// Generals Functions
bool createControls ();
bool showControls (bool visible);
void resize (wildWeasel::alignment &rcNewArea);
};
/*------------------------------------------------------------------------------------
| ----------------------------------------------------------------------------- |
| | | |
| | | |
| | hListViewLayer | |
| | | |
| | | |
| | | |
| | | |
| ----------------------------------------------------------------------------- |
| |
| ------------------------------------- --------------------------------- |
| | | | | |
| | | | hEditOutputBox | |
| | hListViewArray | | | |
| | | | | |
| | | | | |
| | | | | |
| | | | | |
| ------------------------------------- --------------------------------- |
| |
| hLabelCalculationRunning hLabelCalculatingLayer hLabelCalculationAction |
| |
| ------------------- ----------------- ---------------- --------------- |
| hButtonCalcContinue hButtonCalcCancel hButtonCalcPause hButtonCalcTest |
| ------------------- ----------------- ---------------- --------------- |
-----------------------------------------------------------------------------------*/
class miniMaxWinCalcDb
{
protected:
// Calculation variables
wildWeasel::masterMind * ww = nullptr; // pointer to engine
miniMax * pMiniMax = nullptr; // pointer to perfect KI class granting the access to the database
ostream * outputStream = nullptr; // pointer to a stream for the console output of the calculation done by the class miniMax
stringbuf outputStringBuf; // buffer linked to the stream, for reading out of the stream into the buffer
locale myLocale; // for formatting the output
queue<unsigned int> layersToTest; // layer numbers to be tested
thread hThreadSolve;
thread hThreadTestLayer;
bool showingCalculationControls = false;
bool threadSolveIsRunning = false;
bool threadTestLayerIsRunning = false;
condition_variable threadConditionVariable;
mutex threadMutex;
// positions, metrics, sizes, dimensions
unsigned int listViewRowHeight = 20; // height in pixel of a single row
const float defPixelDist = 15; //
const float labelHeight = 30; //
const float buttonHeight = 30; //
// Calculation Functions
void buttonFuncCalcStartOrContinue (void* pUser);
void buttonFuncCalcCancel (void* pUser);
void buttonFuncCalcPause (void* pUser);
void buttonFuncCalcTest ();
void buttonFuncCalcTestAll (void* pUser);
void buttonFuncCalcTestLayer (void* pUser);
void lvSelectedLayerChanged (unsigned int row, unsigned int col, wildWeasel::guiElemEvFol* guiElem, void* pUser);
static void updateOutputControls (void* pUser);
void updateListItemLayer (unsigned int layerNumber);
void updateListItemArray (miniMax::arrayInfoChange infoChange);
void threadSolve ();
void threadProcTestLayer ();
public:
// Constructor / destructor
miniMaxWinCalcDb (wildWeasel::masterMind* ww, miniMax* pMiniMax, wildWeasel::alignment& amCalculation, wildWeasel::font2D* font, wildWeasel::texture* textureLine);
~miniMaxWinCalcDb ();
// Generals Functions
bool createControls ();
void resize (wildWeasel::alignment &amNewArea);
bool showControls (bool visible);
bool isCalculationOngoing ();
miniMax * getMinimaxPointer () { return pMiniMax; };
CRITICAL_SECTION * getCriticalSectionOutput () { return &pMiniMax->csOsPrint; };
};
#endif

View File

@ -0,0 +1,669 @@
/*********************************************************************
miniMax_alphaBetaAlgorithmn.cpp
Copyright (c) Thomas Weber. All rights reserved.
Licensed under the MIT License.
https://github.com/madweasel/madweasels-cpp
\*********************************************************************/
#include "miniMax.h"
//-----------------------------------------------------------------------------
// Name: calcKnotValuesByAlphaBeta()
// Desc: return value is true if calculation is stopped either by user or by an error
//-----------------------------------------------------------------------------
bool miniMax::calcKnotValuesByAlphaBeta(unsigned int layerNumber)
{
// locals
alphaBetaGlobalVars alphaBetaVars(this, layerNumber); // multi-thread vars
// Version 10:
PRINT(1, this, "*** Calculate layer " << layerNumber << " by alpha-beta-algorithmn ***" << endl);
curCalculationActionId = MM_ACTION_PERFORM_ALPHA_BETA;
// initialization
PRINT(2, this, " Bytes in memory: " << memoryUsed2 << endl);
if (!initAlphaBeta(alphaBetaVars)) { return false; }
// run alpha-beta algorithmn
PRINT(2, this, " Bytes in memory: " << memoryUsed2 << endl);
if (!runAlphaBeta(alphaBetaVars)) { return false; }
// update layerStats[].numWonStates, etc.
PRINT(2, this, " Bytes in memory: " << memoryUsed2 << endl);
showLayerStats(layerNumber);
return true;
}
//-----------------------------------------------------------------------------
// Name: saveKnotValueInDatabase()
// Desc:
//-----------------------------------------------------------------------------
void miniMax::alphaBetaSaveInDatabase(unsigned int threadNo, unsigned int layerNumber, unsigned int stateNumber, twoBit knotValue, plyInfoVarType plyValue, bool invertValue)
{
// locals
unsigned int * symStateNumbers = NULL;
unsigned int numSymmetricStates;
unsigned int sysStateNumber;
unsigned int i;
// invert value ?
if (knotValue > SKV_VALUE_GAME_WON) while (true);
if (invertValue) knotValue = skvPerspectiveMatrix[knotValue][PL_TO_MOVE_UNCHANGED];
// get numbers of symmetric states
getSymStateNumWithDoubles(threadNo, &numSymmetricStates, &symStateNumbers);
// save
saveKnotValueInDatabase(layerNumber, stateNumber, knotValue);
savePlyInfoInDatabase (layerNumber, stateNumber, plyValue);
// save value for all symmetric states
for (i=0; i<numSymmetricStates; i++) {
// get state number
sysStateNumber = symStateNumbers[i];
// don't save original state twice
if (sysStateNumber == stateNumber) continue;
// save
saveKnotValueInDatabase(layerNumber, sysStateNumber, knotValue);
savePlyInfoInDatabase (layerNumber, sysStateNumber, plyValue);
}
}
//-----------------------------------------------------------------------------
// Name: initAlphaBeta()
// Desc: The function setSituation is called for each state to mark the invalid ones.
//-----------------------------------------------------------------------------
bool miniMax::initAlphaBeta(alphaBetaGlobalVars &alphaBetaVars)
{
// locals
bufferedFileClass* invalidArray; //
bool initAlreadyDone = false; // true if the initialization information is already available in a file
stringstream ssInvArrayDirectory; //
stringstream ssInvArrayFilePath; //
// set current processed layer number
PRINT(1, this, endl << " *** Signing of invalid states for layer " << alphaBetaVars.layerNumber << " (" << (getOutputInformation(alphaBetaVars.layerNumber)) << ") which has " << layerStats[alphaBetaVars.layerNumber].knotsInLayer << " knots ***");
// file names
ssInvArrayDirectory.str(""); ssInvArrayDirectory << fileDirectory << (fileDirectory.size()?"\\":"") << "invalidStates";
ssInvArrayFilePath .str(""); ssInvArrayFilePath << fileDirectory << (fileDirectory.size()?"\\":"") << "invalidStates\\invalidStatesOfLayer" << alphaBetaVars.layerNumber << ".dat";
// does initialization file exist ?
CreateDirectoryA(ssInvArrayDirectory.str().c_str(), NULL);
invalidArray = new bufferedFileClass(threadManager.getNumThreads(), FILE_BUFFER_SIZE, ssInvArrayFilePath.str().c_str());
if (invalidArray->getFileSize() == (LONGLONG) layerStats[alphaBetaVars.layerNumber].knotsInLayer) {
PRINT(2, this, " Loading invalid states from file: " << ssInvArrayFilePath.str());
initAlreadyDone = true;
}
// prepare parameters
numStatesProcessed = 0;
alphaBetaVars.statsValueCounter[SKV_VALUE_GAME_WON ] = 0;
alphaBetaVars.statsValueCounter[SKV_VALUE_GAME_LOST ] = 0;
alphaBetaVars.statsValueCounter[SKV_VALUE_GAME_DRAWN] = 0;
alphaBetaVars.statsValueCounter[SKV_VALUE_INVALID ] = 0;
threadManagerClass::threadVarsArray<initAlphaBetaVars> tva(threadManager.getNumThreads(), initAlphaBetaVars(this, &alphaBetaVars, alphaBetaVars.layerNumber, invalidArray, initAlreadyDone));
// process each state in the current layer
switch (threadManager.executeParallelLoop(initAlphaBetaThreadProc, tva.getPointerToArray(), tva.getSizeOfArray(), TM_SCHEDULE_STATIC, 0, layerStats[alphaBetaVars.layerNumber].knotsInLayer - 1, 1))
{
case TM_RETURN_VALUE_OK:
break;
case TM_RETURN_VALUE_EXECUTION_CANCELLED:
PRINT(0,this, "\n****************************************\nMain thread: Execution cancelled by user!\n****************************************\n");
SAFE_DELETE(invalidArray);
return false;
default:
case TM_RETURN_VALUE_INVALID_PARAM:
case TM_RETURN_VALUE_UNEXPECTED_ERROR:
PRINT(0,this, "\n****************************************\nMain thread: Invalid or unexpected param!\n****************************************\n");
return falseOrStop();
}
// reduce and delete thread specific data
tva.reduce();
if (numStatesProcessed < layerStats[alphaBetaVars.layerNumber].knotsInLayer) {
SAFE_DELETE(invalidArray);
return falseOrStop();
}
invalidArray->flushBuffers();
SAFE_DELETE(invalidArray);
// when init file was created new then save it now
PRINT(2, this, " Saved initialized states to file: " << ssInvArrayFilePath.str());
// show statistics
PRINT(2, this, " won states: " << alphaBetaVars.statsValueCounter[SKV_VALUE_GAME_WON ]);
PRINT(2, this, " lost states: " << alphaBetaVars.statsValueCounter[SKV_VALUE_GAME_LOST ]);
PRINT(2, this, " draw states: " << alphaBetaVars.statsValueCounter[SKV_VALUE_GAME_DRAWN]);
PRINT(2, this, " invalid states: " << alphaBetaVars.statsValueCounter[SKV_VALUE_INVALID ]);
return true;
}
//-----------------------------------------------------------------------------
// Name: initAlphaBetaThreadProc()
// Desc: set short knot value to SKV_VALUE_INVALID, ply info to PLYINFO_VALUE_INVALID and knotAlreadyCalculated to true or false, whether setSituation() returns true or false
//-----------------------------------------------------------------------------
DWORD miniMax::initAlphaBetaThreadProc(void* pParameter, int index)
{
// locals
initAlphaBetaVars * iabVars = (initAlphaBetaVars *) pParameter;
miniMax * m = iabVars->pMiniMax;
float floatValue; // dummy variable for calls of getValueOfSituation()
stateAdressStruct curState; // current state counter for loops
twoBit curStateValue = 0; // for calls of getValueOfSituation()
plyInfoVarType plyInfo; // depends on the curStateValue
curState.layerNumber = iabVars->layerNumber;
curState.stateNumber = index;
iabVars->statesProcessed++;
// print status
if (iabVars->statesProcessed % OUTPUT_EVERY_N_STATES == 0) {
m->numStatesProcessed += OUTPUT_EVERY_N_STATES;
PRINT(2, m, "Already initialized " << m->numStatesProcessed << " of " << m->layerStats[curState.layerNumber].knotsInLayer << " states");
}
// layer initialization already done ? if so, then read from file
if (iabVars->initAlreadyDone) {
if (!iabVars->bufferedFile->readBytes(iabVars->curThreadNo, index * sizeof(twoBit), sizeof(twoBit), (unsigned char*) &curStateValue)) {
PRINT(0, m, "ERROR: initArray->takeBytes() failed");
return m->falseOrStop();
}
// initialization not done
} else {
// set current selected situation
if (!m->setSituation(iabVars->curThreadNo, curState.layerNumber, curState.stateNumber)) {
curStateValue = SKV_VALUE_INVALID;
} else {
// get value of current situation
m->getValueOfSituation(iabVars->curThreadNo, floatValue, curStateValue);
}
}
// calc ply info
if (curStateValue == SKV_VALUE_GAME_WON || curStateValue == SKV_VALUE_GAME_LOST) {
plyInfo = 0;
} else if (curStateValue == SKV_VALUE_INVALID) {
plyInfo = PLYINFO_VALUE_INVALID;
} else {
plyInfo = PLYINFO_VALUE_UNCALCULATED;
}
// save short knot value & ply info (m->alphaBetaSaveInDatabase(iabVars->curThreadNo, curStateValue, plyInfo, false); ???)
m->saveKnotValueInDatabase(curState.layerNumber, curState.stateNumber, curStateValue);
m->savePlyInfoInDatabase (curState.layerNumber, curState.stateNumber, plyInfo);
// write data to file
if (!iabVars->initAlreadyDone) {
if (!iabVars->bufferedFile->writeBytes(iabVars->curThreadNo, index * sizeof(twoBit), sizeof(twoBit), (unsigned char*) &curStateValue)) {
PRINT(0, m, "ERROR: bufferedFile->writeBytes failed!");
return m->falseOrStop();
}
}
iabVars->statsValueCounter[curStateValue]++;
return TM_RETURN_VALUE_OK;
}
//-----------------------------------------------------------------------------
// Name: runAlphaBeta()
// Desc:
//-----------------------------------------------------------------------------
bool miniMax::runAlphaBeta(alphaBetaGlobalVars &alphaBetaVars)
{
// prepare parameters
PRINT(1, this, " Calculate layer " << alphaBetaVars.layerNumber << " with function letTheTreeGrow():");
numStatesProcessed = 0;
alphaBetaVars.statsValueCounter[SKV_VALUE_GAME_WON ] = 0;
alphaBetaVars.statsValueCounter[SKV_VALUE_GAME_LOST ] = 0;
alphaBetaVars.statsValueCounter[SKV_VALUE_GAME_DRAWN] = 0;
alphaBetaVars.statsValueCounter[SKV_VALUE_INVALID ] = 0;
threadManagerClass::threadVarsArray<runAlphaBetaVars> tva(threadManager.getNumThreads(), runAlphaBetaVars(this, &alphaBetaVars, alphaBetaVars.layerNumber));
// so far no multi-threadin implemented
threadManager.setNumThreads(1);
// process each state in the current layer
switch (threadManager.executeParallelLoop(runAlphaBetaThreadProc, tva.getPointerToArray(), tva.getSizeOfArray(), TM_SCHEDULE_STATIC, 0, layerStats[alphaBetaVars.layerNumber].knotsInLayer - 1, 1))
{
case TM_RETURN_VALUE_OK:
break;
case TM_RETURN_VALUE_EXECUTION_CANCELLED:
PRINT(0,this, "\n****************************************\nMain thread: Execution cancelled by user!\n****************************************\n");
return false;
default:
case TM_RETURN_VALUE_INVALID_PARAM:
case TM_RETURN_VALUE_UNEXPECTED_ERROR:
return falseOrStop();
}
threadManager.setNumThreads(4);
// reduce and delete thread specific data
tva.reduce();
if (numStatesProcessed < layerStats[alphaBetaVars.layerNumber].knotsInLayer) return falseOrStop();
// show statistics
PRINT(2, this, " won states: " << alphaBetaVars.statsValueCounter[SKV_VALUE_GAME_WON ]);
PRINT(2, this, " lost states: " << alphaBetaVars.statsValueCounter[SKV_VALUE_GAME_LOST ]);
PRINT(2, this, " draw states: " << alphaBetaVars.statsValueCounter[SKV_VALUE_GAME_DRAWN]);
PRINT(2, this, " invalid states: " << alphaBetaVars.statsValueCounter[SKV_VALUE_INVALID ]);
return true;
}
//-----------------------------------------------------------------------------
// Name: runAlphaBetaThreadProc()
// Desc:
//-----------------------------------------------------------------------------
DWORD miniMax::runAlphaBetaThreadProc(void* pParameter, int index)
{
// locals
runAlphaBetaVars * rabVars = (runAlphaBetaVars *) pParameter;
miniMax * m = rabVars->pMiniMax;
stateAdressStruct curState; // current state counter for loops
knotStruct root; //
plyInfoVarType plyInfo; // depends on the curStateValue
curState.layerNumber = rabVars->layerNumber;
curState.stateNumber = index;
rabVars->statesProcessed++;
// print status
if (rabVars->statesProcessed % OUTPUT_EVERY_N_STATES == 0) {
m->numStatesProcessed += OUTPUT_EVERY_N_STATES;
PRINT(2, m, " Processed " << m->numStatesProcessed << " of " << m->layerStats[curState.layerNumber].knotsInLayer << " states");
}
// Version 10: state already calculated? if so leave.
m->readPlyInfoFromDatabase(curState.layerNumber, curState.stateNumber, plyInfo);
if (plyInfo != PLYINFO_VALUE_UNCALCULATED) return TM_RETURN_VALUE_OK;
// set current selected situation
if (m->setSituation(rabVars->curThreadNo, curState.layerNumber, curState.stateNumber)) {
// calc value of situation
m->letTheTreeGrow(&root, rabVars, m->depthOfFullTree, SKV_VALUE_GAME_LOST, SKV_VALUE_GAME_WON);
} else {
// should not occur, because already tested by plyInfo == PLYINFO_VALUE_UNCALCULATED
MessageBoxW(NULL, L"This event should never occur. if (!m->setSituation())", L"ERROR", MB_OK);
}
return TM_RETURN_VALUE_OK;
}
//-----------------------------------------------------------------------------
// Name: letTheTreeGrow()
// Desc:
//-----------------------------------------------------------------------------
void miniMax::letTheTreeGrow(knotStruct *knot, runAlphaBetaVars *rabVars, unsigned int tilLevel, float alpha, float beta)
{
// Locals
void *pPossibilities;
unsigned int *idPossibility;
unsigned int layerNumber = 0; // layer number of current state
unsigned int stateNumber = 0; // state number of current state
unsigned int maxWonfreqValuesSubMoves = 0;
// standard values
knot->branches = &rabVars->branchArray[(depthOfFullTree - tilLevel) * maxNumBranches];
knot->numPossibilities = 0;
knot->bestBranch = 0;
knot->bestMoveId = 0;
knot->isOpponentLevel = getOpponentLevel(rabVars->curThreadNo);
knot->plyInfo = PLYINFO_VALUE_UNCALCULATED;
knot->shortValue = SKV_VALUE_GAME_DRAWN;
knot->floatValue = (float) knot->shortValue;
// evaluate situation, musn't occur while calculating database
if (tilLevel == 0) {
if (calcDatabase) {
// if tilLevel is equal zero it means that memory is gone out, since each recursive step needs memory
PRINT(0, this, "ERROR: tilLevel == 0");
knot->shortValue = SKV_VALUE_INVALID;
knot->plyInfo = PLYINFO_VALUE_INVALID;
knot->floatValue = (float) knot->shortValue;
falseOrStop();
} else {
getValueOfSituation(rabVars->curThreadNo, knot->floatValue, knot->shortValue);
}
// investigate branches
} else {
// get layer and state number of current state and look if short knot value can be found in database or in an array
if (alphaBetaTryDataBase(knot, rabVars, tilLevel, layerNumber, stateNumber)) return;
// get number of possiblities
idPossibility = getPossibilities(rabVars->curThreadNo, &knot->numPossibilities, &knot->isOpponentLevel, &pPossibilities);
// unable to move
if (knot->numPossibilities == 0) {
// if unable to move a final state is reached
knot->plyInfo = 0;
getValueOfSituation(rabVars->curThreadNo, knot->floatValue, knot->shortValue);
if (tilLevel == depthOfFullTree - 1) rabVars->freqValuesSubMoves[knot->shortValue]++;
// if unable to move an invalid state was reached if nobody has won
if (calcDatabase && knot->shortValue == SKV_VALUE_GAME_DRAWN) {
knot->shortValue = SKV_VALUE_INVALID;
knot->plyInfo = PLYINFO_VALUE_INVALID;
knot->floatValue = (float) knot->shortValue;
}
// movement is possible
} else {
// move, letTreeGroe, undo
alphaBetaTryPossibilites(knot, rabVars, tilLevel, idPossibility, pPossibilities, maxWonfreqValuesSubMoves, alpha, beta);
// calculate value of knot - its the value of the best branch
alphaBetaCalcKnotValue(knot);
// calc ply info
alphaBetaCalcPlyInfo(knot);
// select randomly one of the best moves, if they are equivalent
alphaBetaChooseBestMove(knot, rabVars, tilLevel, idPossibility, maxWonfreqValuesSubMoves);
}
// save value and best branch into database and set value as valid
if (calcDatabase && hFileShortKnotValues && hFilePlyInfo) alphaBetaSaveInDatabase(rabVars->curThreadNo, layerNumber, stateNumber, knot->shortValue, knot->plyInfo, knot->isOpponentLevel);
}
}
//-----------------------------------------------------------------------------
// Name: alphaBetaTryDataBase()
// Desc:
// 1 - Determines layerNumber and stateNumber for the given game situation.
// 2 - Look into database if knot value and ply info are already calculated. If so sets knot->shortValue, knot->floatValue and knot->plyInfo.
// CAUTION: knot->isOpponentLevel must be set and valid.
//-----------------------------------------------------------------------------
bool miniMax::alphaBetaTryDataBase(knotStruct *knot, runAlphaBetaVars *rabVars, unsigned int tilLevel, unsigned int &layerNumber, unsigned int &stateNumber)
{
// locals
bool invalidLayerOrStateNumber;
bool subLayerInDatabaseAndCompleted;
twoBit shortKnotValue = SKV_VALUE_INVALID;
plyInfoVarType plyInfo = PLYINFO_VALUE_UNCALCULATED;
// use database ?
if (hFilePlyInfo != NULL && hFileShortKnotValues != NULL && (calcDatabase || layerInDatabase)) {
// situation already existend in database ?
readKnotValueFromDatabase(rabVars->curThreadNo, layerNumber, stateNumber, shortKnotValue, invalidLayerOrStateNumber, subLayerInDatabaseAndCompleted);
readPlyInfoFromDatabase(layerNumber, stateNumber, plyInfo);
// it was possible to achieve an invalid state using move(),
// so the original state was an invalid one
if ((tilLevel < depthOfFullTree && invalidLayerOrStateNumber)
|| (tilLevel < depthOfFullTree && shortKnotValue == SKV_VALUE_INVALID && subLayerInDatabaseAndCompleted)
|| (tilLevel < depthOfFullTree && shortKnotValue == SKV_VALUE_INVALID && plyInfo != PLYINFO_VALUE_UNCALCULATED)) { // version 22: replaced: curCalculatedLayer == layerNumber && knot->plyInfo != PLYINFO_VALUE_UNCALCULATED)) {
knot->shortValue = SKV_VALUE_INVALID;
knot->plyInfo = PLYINFO_VALUE_INVALID;
knot->floatValue = (float) knot->shortValue;
return true;
}
// print out put, if not calculating database, but requesting a knot value
if (shortKnotValue != SKV_VALUE_INVALID && tilLevel == depthOfFullTree && !calcDatabase && subLayerInDatabaseAndCompleted) {
PRINT(2, this, "This state is marked as " << ((shortKnotValue == SKV_VALUE_GAME_WON) ? "WON" : ((shortKnotValue == SKV_VALUE_GAME_LOST) ? "LOST" : ((shortKnotValue == SKV_VALUE_GAME_DRAWN) ? "DRAW" : "INVALID"))) << endl);
}
// when knot value is valid then return best branch
if (calcDatabase && tilLevel < depthOfFullTree && shortKnotValue != SKV_VALUE_INVALID && plyInfo != PLYINFO_VALUE_UNCALCULATED
|| !calcDatabase && tilLevel < depthOfFullTree - 1 && shortKnotValue != SKV_VALUE_INVALID) {
// switch if is not opponent level
if (knot->isOpponentLevel) knot->shortValue = skvPerspectiveMatrix[shortKnotValue][PL_TO_MOVE_UNCHANGED];
else knot->shortValue = skvPerspectiveMatrix[shortKnotValue][PL_TO_MOVE_CHANGED ];
knot->floatValue = (float) knot->shortValue;
knot->plyInfo = plyInfo;
return true;
}
}
return false;
}
//-----------------------------------------------------------------------------
// Name: alphaBetaTryPossibilites()
// Desc:
//-----------------------------------------------------------------------------
void miniMax::alphaBetaTryPossibilites(knotStruct *knot, runAlphaBetaVars *rabVars, unsigned int tilLevel, unsigned int *idPossibility, void *pPossibilities, unsigned int &maxWonfreqValuesSubMoves, float &alpha, float &beta)
{
// locals
void * pBackup;
unsigned int curPoss;
for (curPoss=0; curPoss<knot->numPossibilities; curPoss++) {
// output
if (tilLevel == depthOfFullTree && !calcDatabase) {
printMoveInformation(rabVars->curThreadNo, idPossibility[curPoss], pPossibilities);
rabVars->freqValuesSubMoves[SKV_VALUE_INVALID ] = 0;
rabVars->freqValuesSubMoves[SKV_VALUE_GAME_LOST ] = 0;
rabVars->freqValuesSubMoves[SKV_VALUE_GAME_DRAWN] = 0;
rabVars->freqValuesSubMoves[SKV_VALUE_GAME_WON ] = 0;
}
// move
move(rabVars->curThreadNo, idPossibility[curPoss], knot->isOpponentLevel, &pBackup, pPossibilities);
// recursive call
letTheTreeGrow(&knot->branches[curPoss], rabVars, tilLevel - 1, alpha, beta);
// undo move
undo(rabVars->curThreadNo, idPossibility[curPoss], knot->isOpponentLevel, pBackup, pPossibilities);
// output
if (tilLevel == depthOfFullTree && !calcDatabase) {
rabVars->freqValuesSubMovesBranchWon[curPoss] = rabVars->freqValuesSubMoves[SKV_VALUE_GAME_WON];
if (rabVars->freqValuesSubMoves[SKV_VALUE_GAME_WON] > maxWonfreqValuesSubMoves && knot->branches[curPoss].shortValue == SKV_VALUE_GAME_DRAWN) {
maxWonfreqValuesSubMoves = rabVars->freqValuesSubMoves[SKV_VALUE_GAME_WON];
}
if (hFileShortKnotValues != NULL && layerInDatabase) {
storeValueOfMove(rabVars->curThreadNo, idPossibility[curPoss], pPossibilities, knot->branches[curPoss].shortValue, rabVars->freqValuesSubMoves, knot->branches[curPoss].plyInfo);
PRINT(0, this, "\t: " << ((knot->branches[curPoss].shortValue == SKV_VALUE_GAME_WON) ? "WON" : ((knot->branches[curPoss].shortValue == SKV_VALUE_GAME_LOST) ? "LOST" : ((knot->branches[curPoss].shortValue == SKV_VALUE_GAME_DRAWN) ? "DRAW" : "INVALID"))) << endl);
} else {
PRINT(0, this, "\t: " << knot->branches[curPoss].floatValue << endl);
}
} else if (tilLevel == depthOfFullTree - 1 && !calcDatabase) {
rabVars->freqValuesSubMoves[knot->branches[curPoss].shortValue]++;
}
// don't use alpha beta if using database
if (hFileShortKnotValues != NULL && calcDatabase) continue;
if (hFileShortKnotValues != NULL && tilLevel + 1 >= depthOfFullTree) continue;
// alpha beta algorithmn
if (!knot->isOpponentLevel) {
if (knot->branches[curPoss].floatValue >= beta ) {
knot->numPossibilities = curPoss + 1;
break;
} else if (knot->branches[curPoss].floatValue > alpha) {
alpha = knot->branches[curPoss].floatValue;
}
} else {
if (knot->branches[curPoss].floatValue <= alpha) {
knot->numPossibilities = curPoss + 1;
break;
} else if (knot->branches[curPoss].floatValue < beta ) {
beta = knot->branches[curPoss].floatValue;
}
}
}
// let delete pPossibilities
if (tilLevel < depthOfFullTree) {
deletePossibilities(rabVars->curThreadNo, pPossibilities);
} else {
pRootPossibilities = pPossibilities;
}
}
//-----------------------------------------------------------------------------
// Name: alphaBetaCalcKnotValue()
// Desc:
//-----------------------------------------------------------------------------
void miniMax::alphaBetaCalcKnotValue(knotStruct *knot)
{
// locals
float maxValue = knot->branches[0].floatValue;
unsigned int maxBranch = 0;
unsigned int i;
// opponent tries to minimize the value
if (knot->isOpponentLevel) {
for (i=1; i<knot->numPossibilities; i++) {
// version 21: it should be impossible that knot->shortValue is equal SKV_VALUE_INVALID
if (/*knot->shortValue != SKV_VALUE_INVALID && */knot->branches[i].floatValue < maxValue) {
maxValue = knot->branches[i].floatValue;
maxBranch = i;
}
}
// maximize the value
} else {
for (i=1; i<knot->numPossibilities; i++) {
if (/*knot->shortValue != SKV_VALUE_INVALID && */knot->branches[i].floatValue > maxValue) {
maxValue = knot->branches[i].floatValue;
maxBranch = i;
}
}
}
// set value
knot->floatValue = knot->branches[maxBranch].floatValue;
knot->shortValue = knot->branches[maxBranch].shortValue;
}
//-----------------------------------------------------------------------------
// Name: alphaBetaCalcPlyInfo()
// Desc:
//-----------------------------------------------------------------------------
void miniMax::alphaBetaCalcPlyInfo(knotStruct *knot)
{
// locals
unsigned int i;
unsigned int maxBranch;
plyInfoVarType maxPlyInfo;
twoBit shortKnotValue;
//
if (knot->shortValue == SKV_VALUE_GAME_DRAWN) {
knot->plyInfo = PLYINFO_VALUE_DRAWN;
} else if (knot->shortValue == SKV_VALUE_INVALID) {
knot->plyInfo = PLYINFO_VALUE_INVALID;
} else {
// calculate value of knot
shortKnotValue = (knot->isOpponentLevel) ? skvPerspectiveMatrix[knot->shortValue][PL_TO_MOVE_UNCHANGED] : knot->shortValue;
maxPlyInfo = (shortKnotValue == SKV_VALUE_GAME_WON) ? PLYINFO_VALUE_DRAWN : 0;
maxBranch = 0;
// when current knot is a won state
if (shortKnotValue == SKV_VALUE_GAME_WON) {
for (i=0; i<knot->numPossibilities; i++) {
// invert knot value if necessary
shortKnotValue = (knot->branches[i].isOpponentLevel) ? skvPerspectiveMatrix[knot->branches[i].shortValue][PL_TO_MOVE_UNCHANGED] : knot->branches[i].shortValue;
// take the minimum of the lost states (negative float values)
if ((knot->branches[i].plyInfo < maxPlyInfo && shortKnotValue == SKV_VALUE_GAME_LOST && knot->isOpponentLevel != knot->branches[i].isOpponentLevel)
// after this move the same player will continue, so take the minimum of the won states
|| (knot->branches[i].plyInfo < maxPlyInfo && shortKnotValue == SKV_VALUE_GAME_WON && knot->isOpponentLevel == knot->branches[i].isOpponentLevel)) {
maxPlyInfo = knot->branches[i].plyInfo;
maxBranch = i;
}
}
// current state is a lost state
} else {
for (i=0; i<knot->numPossibilities; i++) {
// invert knot value if necessary
shortKnotValue = (knot->branches[i].isOpponentLevel) ? skvPerspectiveMatrix[knot->branches[i].shortValue][PL_TO_MOVE_UNCHANGED] : knot->branches[i].shortValue;
// after this move the same player will continue, so take the maximum of the lost states (negative float values)
if ((knot->branches[i].plyInfo > maxPlyInfo && shortKnotValue == SKV_VALUE_GAME_WON && knot->isOpponentLevel != knot->branches[i].isOpponentLevel)
// take the maximum of the won states, since that's the longest path
|| (knot->branches[i].plyInfo > maxPlyInfo && shortKnotValue == SKV_VALUE_GAME_LOST && knot->isOpponentLevel == knot->branches[i].isOpponentLevel)) {
maxPlyInfo = knot->branches[i].plyInfo;
maxBranch = i;
}
}
}
// set value
knot->plyInfo = knot->branches[maxBranch].plyInfo + 1;
}
}
//-----------------------------------------------------------------------------
// Name: alphaBetaChooseBestMove()
// Desc: select randomly one of the best moves, if they are equivalent
//-----------------------------------------------------------------------------
void miniMax::alphaBetaChooseBestMove(knotStruct *knot, runAlphaBetaVars *rabVars, unsigned int tilLevel, unsigned int *idPossibility, unsigned int maxWonfreqValuesSubMoves)
{
// locals
float dif;
unsigned int numBestChoices = 0;
unsigned int *bestBranches = new unsigned int[maxNumBranches];
unsigned int i;
unsigned int maxBranch;
// select randomly one of the best moves, if they are equivalent
if (tilLevel == depthOfFullTree && !calcDatabase) {
// check every possible move
for (numBestChoices=0, i=0; i<knot->numPossibilities; i++) {
// use information in database
if (layerInDatabase && hFileShortKnotValues != NULL) {
// selected move with equal knot value
if (knot->branches[i].shortValue == knot->shortValue) {
// best move lead to drawn state
if (knot->shortValue == SKV_VALUE_GAME_DRAWN) {
if (maxWonfreqValuesSubMoves == rabVars->freqValuesSubMovesBranchWon[i]) {
bestBranches[numBestChoices] = i;
numBestChoices++;
}
// best move lead to lost or won state
} else {
if (knot->plyInfo == knot->branches[i].plyInfo + 1) {
bestBranches[numBestChoices] = i;
numBestChoices++;
}
}
}
// conventionell mini-max algorithm
} else {
dif = knot->branches[i].floatValue - knot->floatValue;
dif = (dif > 0) ? dif : -1.0f * dif;
if (dif < FPKV_THRESHOLD) {
bestBranches[numBestChoices] = i;
numBestChoices++;
}
}
}
}
// set value
maxBranch = (numBestChoices ? bestBranches[rand() % numBestChoices] : 0);
knot->bestMoveId = idPossibility[maxBranch];
knot->bestBranch = maxBranch;
SAFE_DELETE_ARRAY(bestBranches);
}

View File

@ -0,0 +1,673 @@
/*********************************************************************
miniMax_database.cpp
Copyright (c) Thomas Weber. All rights reserved.
Licensed under the MIT License.
https://github.com/madweasel/madweasels-cpp
\*********************************************************************/
#include "miniMax.h"
//-----------------------------------------------------------------------------
// Name: ~miniMax()
// Desc: miniMax class destructor
//-----------------------------------------------------------------------------
void miniMax::closeDatabase()
{
// close database
if (hFileShortKnotValues != NULL) {
unloadAllLayers();
SAFE_DELETE_ARRAY(layerStats);
CloseHandle(hFileShortKnotValues);
hFileShortKnotValues = NULL;
}
// close ply information file
if (hFilePlyInfo != NULL) {
unloadAllPlyInfos();
SAFE_DELETE_ARRAY(plyInfos);
CloseHandle(hFilePlyInfo);
hFilePlyInfo = NULL;
}
}
//-----------------------------------------------------------------------------
// Name: unloadPlyInfo()
// Desc:
//-----------------------------------------------------------------------------
void miniMax::unloadPlyInfo(unsigned int layerNumber)
{
plyInfoStruct * myPis = &plyInfos[layerNumber];
memoryUsed2 -= myPis->sizeInBytes;
arrayInfos.removeArray(layerNumber, arrayInfoStruct::arrayType_plyInfos, myPis->sizeInBytes, 0);
SAFE_DELETE_ARRAY(myPis->plyInfo);
myPis->plyInfoIsLoaded = false;
}
//-----------------------------------------------------------------------------
// Name: unloadLayer()
// Desc:
//-----------------------------------------------------------------------------
void miniMax::unloadLayer(unsigned int layerNumber)
{
layerStatsStruct * myLss = &layerStats[layerNumber];
SAFE_DELETE_ARRAY(myLss->shortKnotValueByte);
memoryUsed2 -= myLss->sizeInBytes;
arrayInfos.removeArray(layerNumber, arrayInfoStruct::arrayType_layerStats, myLss->sizeInBytes, 0);
myLss->layerIsLoaded = false;
}
//-----------------------------------------------------------------------------
// Name: unloadAllPlyInfos()
// Desc:
//-----------------------------------------------------------------------------
void miniMax::unloadAllPlyInfos()
{
for (unsigned int i=0; i<plyInfoHeader.numLayers; i++) {
unloadPlyInfo(i);
}
}
//-----------------------------------------------------------------------------
// Name: unloadAllLayers()
// Desc:
//-----------------------------------------------------------------------------
void miniMax::unloadAllLayers()
{
for (unsigned int i=0; i<skvfHeader.numLayers; i++) {
unloadLayer(i);
}
}
//-----------------------------------------------------------------------------
// Name: saveBytesToFile()
// Desc:
//-----------------------------------------------------------------------------
void miniMax::saveBytesToFile(HANDLE hFile, long long offset, unsigned int numBytes, void *pBytes)
{
DWORD dwBytesWritten;
LARGE_INTEGER liDistanceToMove;
unsigned int restingBytes = numBytes;
void * myPointer = pBytes;
bool errorPrint = false;
liDistanceToMove.QuadPart = offset;
while (errorPrint = !SetFilePointerEx(hFile, liDistanceToMove, NULL, FILE_BEGIN)) {
if (!errorPrint) PRINT(1, this, "ERROR: SetFilePointerEx failed!");
}
while (restingBytes > 0) {
if (WriteFile(hFile, myPointer, restingBytes, &dwBytesWritten, NULL) == TRUE) {
restingBytes -= dwBytesWritten;
myPointer = (void*) (((unsigned char*) myPointer) + dwBytesWritten);
if (restingBytes > 0) PRINT(2, this, "Still " << restingBytes << " to write!");
} else {
if (!errorPrint) PRINT(0, this, "ERROR: WriteFile Failed!");
errorPrint = true;
}
}
}
//-----------------------------------------------------------------------------
// Name: loadBytesFromFile()
// Desc:
//-----------------------------------------------------------------------------
void miniMax::loadBytesFromFile(HANDLE hFile, long long offset, unsigned int numBytes, void *pBytes)
{
DWORD dwBytesRead;
LARGE_INTEGER liDistanceToMove;
unsigned int restingBytes = numBytes;
void * myPointer = pBytes;
bool errorPrint = false;
liDistanceToMove.QuadPart = offset;
while (errorPrint = !SetFilePointerEx(hFile, liDistanceToMove, NULL, FILE_BEGIN)) {
if (!errorPrint) PRINT(0, this, "ERROR: SetFilePointerEx failed!");
}
while (restingBytes > 0) {
if (ReadFile(hFile, pBytes, restingBytes, &dwBytesRead, NULL) == TRUE) {
restingBytes -= dwBytesRead;
myPointer = (void*) (((unsigned char*) myPointer) + dwBytesRead);
if (restingBytes > 0) { PRINT(2, this, "Still " << restingBytes << " bytes to read!"); }
} else {
if (!errorPrint) PRINT(0, this, "ERROR: ReadFile Failed!");
errorPrint = true;
}
}
}
//-----------------------------------------------------------------------------
// Name: isCurrentStateInDatabase()
// Desc:
//-----------------------------------------------------------------------------
bool miniMax::isCurrentStateInDatabase(unsigned int threadNo)
{
unsigned int layerNum, stateNumber;
if (hFileShortKnotValues == NULL) {
return false;
} else {
getLayerAndStateNumber(threadNo, layerNum, stateNumber);
return layerStats[layerNum].layerIsCompletedAndInFile;
}
}
//-----------------------------------------------------------------------------
// Name: saveHeader()
// Desc:
//-----------------------------------------------------------------------------
void miniMax::saveHeader(skvFileHeaderStruct *dbH, layerStatsStruct *lStats)
{
DWORD dwBytesWritten;
SetFilePointer(hFileShortKnotValues, 0, NULL, FILE_BEGIN);
WriteFile(hFileShortKnotValues, dbH, sizeof(skvFileHeaderStruct), &dwBytesWritten, NULL);
WriteFile(hFileShortKnotValues, lStats, sizeof(layerStatsStruct) * dbH->numLayers, &dwBytesWritten, NULL);
}
//-----------------------------------------------------------------------------
// Name: saveHeader()
// Desc:
//-----------------------------------------------------------------------------
void miniMax::saveHeader(plyInfoFileHeaderStruct *piH, plyInfoStruct *pInfo)
{
DWORD dwBytesWritten;
SetFilePointer(hFilePlyInfo, 0, NULL, FILE_BEGIN);
WriteFile(hFilePlyInfo, piH, sizeof(plyInfoFileHeaderStruct), &dwBytesWritten, NULL);
WriteFile(hFilePlyInfo, pInfo, sizeof(plyInfoStruct) * piH->numLayers, &dwBytesWritten, NULL);
}
//-----------------------------------------------------------------------------
// Name: openDatabase()
// Desc:
//-----------------------------------------------------------------------------
bool miniMax::openDatabase(const char *directory, unsigned int maximumNumberOfBranches)
{
if (strlen(directory) && !PathFileExistsA(directory)) {
PRINT(0, this, "ERROR: Database path " << directory << " not valid!");
return falseOrStop();
}
openSkvFile (directory, maximumNumberOfBranches);
openPlyInfoFile (directory);
return true;
}
//-----------------------------------------------------------------------------
// Name: openSkvFile()
// Desc:
//-----------------------------------------------------------------------------
void miniMax::openSkvFile(const char *directory, unsigned int maximumNumberOfBranches)
{
// locals
stringstream ssDatabaseFile;
DWORD dwBytesRead;
unsigned int i;
// don't open file twice
if (hFileShortKnotValues != NULL) return;
// remember directory name
fileDirectory.assign(directory);
ssDatabaseFile << fileDirectory << (strlen(directory)?"\\":"") << "shortKnotValue.dat";
PRINT(2, this, "Open short knot value file: " << fileDirectory << (strlen(directory)?"\\":"") << "shortKnotValue.dat" << endl);
// Open Database-File (FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH | FILE_FLAG_RANDOM_ACCESS)
hFileShortKnotValues = CreateFileA(ssDatabaseFile.str().c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
// opened file succesfully
if (hFileShortKnotValues == INVALID_HANDLE_VALUE) {
hFileShortKnotValues = NULL;
return;
}
// set header to invalid
skvfHeader.headerCode = 0;
maxNumBranches = maximumNumberOfBranches;
// database complete ?
ReadFile(hFileShortKnotValues, &skvfHeader, sizeof(skvFileHeaderStruct), &dwBytesRead, NULL);
// invalid file ?
if (dwBytesRead != sizeof(skvFileHeaderStruct) || skvfHeader.headerCode != SKV_FILE_HEADER_CODE) {
// create default header
skvfHeader.completed = false;
skvfHeader.numLayers = getNumberOfLayers();
skvfHeader.headerCode = SKV_FILE_HEADER_CODE;
skvfHeader.headerAndStatsSize = sizeof(layerStatsStruct) * skvfHeader.numLayers + sizeof(skvFileHeaderStruct);
layerStats = new layerStatsStruct[skvfHeader.numLayers];
layerStats[0].layerOffset = 0;
for (i=0; i<skvfHeader.numLayers; i++) {
getSuccLayers(i, &layerStats[i].numSuccLayers, &layerStats[i].succLayers[0]);
layerStats[i].partnerLayer = getPartnerLayer(i);
layerStats[i].knotsInLayer = getNumberOfKnotsInLayer(i);
layerStats[i].sizeInBytes = (layerStats[i].knotsInLayer + 3) / 4;
layerStats[i].shortKnotValueByte = NULL;
layerStats[i].skvCompressed = NULL;
layerStats[i].layerIsLoaded = false;
layerStats[i].layerIsCompletedAndInFile = false;
layerStats[i].numWonStates = 0;
layerStats[i].numLostStates = 0;
layerStats[i].numDrawnStates = 0;
layerStats[i].numInvalidStates = 0;
}
for (i=1; i<skvfHeader.numLayers; i++) {
layerStats[i].layerOffset = layerStats[i-1].layerOffset + layerStats[i-1].sizeInBytes;
}
// write header
saveHeader(&skvfHeader, layerStats);
// read layer stats
} else {
layerStats = new layerStatsStruct[skvfHeader.numLayers];
ReadFile(hFileShortKnotValues, layerStats, sizeof(layerStatsStruct) * skvfHeader.numLayers, &dwBytesRead, NULL);
for (i=0; i<skvfHeader.numLayers; i++) {
layerStats[i].shortKnotValueByte = NULL;
layerStats[i].skvCompressed = NULL;
}
}
}
//-----------------------------------------------------------------------------
// Name: openPlyInfoFile()
// Desc:
//-----------------------------------------------------------------------------
void miniMax::openPlyInfoFile(const char *directory)
{
// locals
stringstream ssFile;
DWORD dwBytesRead;
unsigned int i;
// don't open file twice
if (hFilePlyInfo != NULL) return;
// remember directory name
ssFile << directory << (strlen(directory)?"\\":"") << "plyInfo.dat";
PRINT(2, this, "Open ply info file: " << ssFile.str() << endl << endl);
// Open Database-File (FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH | FILE_FLAG_RANDOM_ACCESS)
hFilePlyInfo = CreateFileA(ssFile.str().c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
// opened file succesfully
if (hFilePlyInfo == INVALID_HANDLE_VALUE) {
hFilePlyInfo = NULL;
return;
}
// set header to invalid
plyInfoHeader.headerCode = 0;
// database complete ?
ReadFile(hFilePlyInfo, &plyInfoHeader, sizeof(plyInfoHeader), &dwBytesRead, NULL);
// invalid file ?
if (dwBytesRead != sizeof(plyInfoHeader) || plyInfoHeader.headerCode != PLYINFO_HEADER_CODE) {
// create default header
plyInfoHeader.plyInfoCompleted = false;
plyInfoHeader.numLayers = getNumberOfLayers();
plyInfoHeader.headerCode = PLYINFO_HEADER_CODE;
plyInfoHeader.headerAndPlyInfosSize = sizeof(plyInfoStruct) * plyInfoHeader.numLayers + sizeof(plyInfoHeader);
plyInfos = new plyInfoStruct[plyInfoHeader.numLayers];
plyInfos[0].layerOffset = 0;
for (i=0; i<plyInfoHeader.numLayers; i++) {
plyInfos[i].knotsInLayer = getNumberOfKnotsInLayer(i);
plyInfos[i].plyInfo = NULL;
plyInfos[i].plyInfoCompressed = NULL;
plyInfos[i].plyInfoIsLoaded = false;
plyInfos[i].plyInfoIsCompletedAndInFile = false;
plyInfos[i].sizeInBytes = plyInfos[i].knotsInLayer * sizeof(plyInfoVarType);
}
for (i=1; i<plyInfoHeader.numLayers; i++) {
plyInfos[i].layerOffset = plyInfos[i-1].layerOffset + plyInfos[i-1].sizeInBytes;
}
// write header
saveHeader(&plyInfoHeader, plyInfos);
// read layer stats
} else {
plyInfos = new plyInfoStruct[plyInfoHeader.numLayers];
ReadFile(hFilePlyInfo, plyInfos, sizeof(plyInfoStruct) * plyInfoHeader.numLayers, &dwBytesRead, NULL);
for (i=0; i<plyInfoHeader.numLayers; i++) {
plyInfos[i].plyInfo = NULL;
plyInfos[i].plyInfoCompressed = NULL;
}
}
}
//-----------------------------------------------------------------------------
// Name: saveLayerToFile()
// Desc:
//-----------------------------------------------------------------------------
void miniMax::saveLayerToFile(unsigned int layerNumber)
{
// don't save layer and header when only preparing layers
plyInfoStruct * myPis = &plyInfos[layerNumber];
layerStatsStruct * myLss = &layerStats[layerNumber];
if (onlyPrepareLayer) return;
// save layer if there are any states
if (myLss->sizeInBytes) {
// short knot values & ply info
curCalculationActionId = MM_ACTION_SAVING_LAYER_TO_FILE;
saveBytesToFile(hFileShortKnotValues, skvfHeader.headerAndStatsSize + myLss->layerOffset, myLss->sizeInBytes, myLss->shortKnotValueByte);
saveBytesToFile(hFilePlyInfo, plyInfoHeader.headerAndPlyInfosSize + myPis->layerOffset, myPis->sizeInBytes, myPis->plyInfo);
}
// mark layer as completed
myLss->layerIsCompletedAndInFile = true;
myPis->plyInfoIsCompletedAndInFile = true;
}
//-----------------------------------------------------------------------------
// Name: measureIops()
// Desc:
//-----------------------------------------------------------------------------
inline void miniMax::measureIops(long long &numOperations, LARGE_INTEGER &interval, LARGE_INTEGER &curTimeBefore, char text[])
{
// locals
LARGE_INTEGER curTimeAfter;
if (!MEASURE_IOPS) return;
numOperations++; // ... not thread-safe !!!
// only the time for the io-operation is considered and accumulated
if (MEASURE_ONLY_IO) {
QueryPerformanceCounter(&curTimeAfter);
interval.QuadPart += curTimeAfter.QuadPart - curTimeBefore.QuadPart; // ... not thread-safe !!!
double totalTimeGone = (double) interval.QuadPart / frequency.QuadPart; // ... not thread-safe !!!
if (totalTimeGone >= 5.0) {
PRINT(0, this, text << "operations per second for last interval: " << (int) (numOperations / totalTimeGone));
interval.QuadPart = 0; // ... not thread-safe !!!
numOperations = 0; // ... not thread-safe !!!
}
// the whole time passed since the beginning of the interval is considered
} else if (numOperations >= MEASURE_TIME_FREQUENCY) {
QueryPerformanceCounter(&curTimeAfter);
double totalTimeGone = (double) (curTimeAfter.QuadPart - interval.QuadPart) / frequency.QuadPart; // ... not thread-safe !!!
PRINT(0, this, text << "operations per second for last interval: " << numOperations / totalTimeGone);
interval.QuadPart = curTimeAfter.QuadPart; // ... not thread-safe !!!
numOperations = 0; // ... not thread-safe !!!
}
}
//-----------------------------------------------------------------------------
// Name: readKnotValueFromDatabase()
// Desc:
//-----------------------------------------------------------------------------
void miniMax::readKnotValueFromDatabase(unsigned int threadNo, unsigned int &layerNumber, unsigned int &stateNumber, twoBit &knotValue, bool &invalidLayerOrStateNumber, bool &layerInDatabaseAndCompleted)
{
// get state number, since this is the address, where the value is saved
getLayerAndStateNumber(threadNo, layerNumber, stateNumber);
// layer in database and completed ?
layerStatsStruct * myLss = &layerStats[layerNumber];
layerInDatabaseAndCompleted = myLss->layerIsCompletedAndInFile;
// valid state and layer number ?
if (layerNumber > skvfHeader.numLayers || stateNumber > myLss->knotsInLayer) {
invalidLayerOrStateNumber = true;
} else {
invalidLayerOrStateNumber = false; // checkStateIntegrity();
}
if (invalidLayerOrStateNumber) {
knotValue = SKV_VALUE_INVALID;
return;
}
// read
readKnotValueFromDatabase(layerNumber, stateNumber, knotValue);
}
//-----------------------------------------------------------------------------
// Name: readKnotValueFromDatabase()
// Desc:
//-----------------------------------------------------------------------------
void miniMax::readKnotValueFromDatabase(unsigned int layerNumber, unsigned int stateNumber, twoBit &knotValue)
{
// locals
twoBit databaseByte;
long long bytesAllocated;
twoBit defValue = SKV_WHOLE_BYTE_IS_INVALID;
layerStatsStruct * myLss = &layerStats[layerNumber];
// valid state and layer number ?
if (layerNumber > skvfHeader.numLayers || stateNumber > myLss->knotsInLayer) {
PRINT(0, this, "ERROR: INVALID layerNumber OR stateNumber in readKnotValueFromDatabase()!");
knotValue = SKV_VALUE_INVALID;
return;
}
// if database is complete get whole byte from file
if (skvfHeader.completed || layerInDatabase || myLss->layerIsCompletedAndInFile) {
EnterCriticalSection(&csDatabase);
loadBytesFromFile(hFileShortKnotValues, skvfHeader.headerAndStatsSize + myLss->layerOffset + stateNumber / 4, 1, &databaseByte);
LeaveCriticalSection(&csDatabase);
} else {
// is layer already loaded
if (!myLss->layerIsLoaded) {
EnterCriticalSection(&csDatabase);
if (!myLss->layerIsLoaded) {
// if layer is in database and completed, then load layer from file into memory, set default value otherwise
myLss->shortKnotValueByte = new unsigned char[myLss->sizeInBytes];
if (myLss->layerIsCompletedAndInFile) {
loadBytesFromFile(hFileShortKnotValues, skvfHeader.headerAndStatsSize + myLss->layerOffset, myLss->sizeInBytes, myLss->shortKnotValueByte);
} else {
memset(myLss->shortKnotValueByte, SKV_WHOLE_BYTE_IS_INVALID, myLss->sizeInBytes);
}
bytesAllocated = myLss->sizeInBytes;
arrayInfos.addArray(layerNumber, arrayInfoStruct::arrayType_layerStats, myLss->sizeInBytes, 0);
// output
myLss->layerIsLoaded = true;
memoryUsed2 += bytesAllocated;
PRINT(3, this, "Allocated " << bytesAllocated << " bytes in memory for knot values of layer " << layerNumber << ", which is " << (myLss->layerIsCompletedAndInFile?"":" NOT ") << " fully calculated, due to read operation.");
}
LeaveCriticalSection(&csDatabase);
}
// measure io-operations per second
LARGE_INTEGER curTimeBefore;
if (MEASURE_IOPS && MEASURE_ONLY_IO) {
QueryPerformanceCounter(&curTimeBefore);
}
// read ply info from array
databaseByte = myLss->shortKnotValueByte[stateNumber / 4];
// measure io-operations per second
measureIops(numReadSkvOperations, readSkvInterval, curTimeBefore, "Read knot value ");
}
// make half byte
knotValue = _rotr8(databaseByte, 2 * (stateNumber % 4)) & 3;
}
//-----------------------------------------------------------------------------
// Name: readPlyInfoFromDatabase()
// Desc:
//-----------------------------------------------------------------------------
void miniMax::readPlyInfoFromDatabase(unsigned int layerNumber, unsigned int stateNumber, plyInfoVarType &value)
{
// locals
unsigned int curKnot;
plyInfoVarType defValue = PLYINFO_VALUE_UNCALCULATED;
long long bytesAllocated;
plyInfoStruct * myPis = &plyInfos[layerNumber];
// valid state and layer number ?
if (layerNumber > plyInfoHeader.numLayers || stateNumber > myPis->knotsInLayer) {
PRINT(0, this, "ERROR: INVALID layerNumber OR stateNumber in readPlyInfoFromDatabase()!");
value = PLYINFO_VALUE_INVALID;
return;
}
// if database is complete get whole byte from file
if (plyInfoHeader.plyInfoCompleted || layerInDatabase || myPis->plyInfoIsCompletedAndInFile) {
EnterCriticalSection(&csDatabase);
loadBytesFromFile(hFilePlyInfo, plyInfoHeader.headerAndPlyInfosSize + myPis->layerOffset + sizeof(plyInfoVarType) * stateNumber, sizeof(plyInfoVarType), &value);
LeaveCriticalSection(&csDatabase);
} else {
// is layer already in memory?
if (!myPis->plyInfoIsLoaded) {
EnterCriticalSection(&csDatabase);
if (!myPis->plyInfoIsLoaded) {
// if layer is in database and completed, then load layer from file into memory; set default value otherwise
myPis->plyInfo = new plyInfoVarType[myPis->knotsInLayer];
if (myPis->plyInfoIsCompletedAndInFile) {
loadBytesFromFile(hFilePlyInfo, plyInfoHeader.headerAndPlyInfosSize + myPis->layerOffset, myPis->sizeInBytes, myPis->plyInfo);
} else {
for (curKnot=0; curKnot<myPis->knotsInLayer; curKnot++) { myPis->plyInfo[curKnot] = defValue; }
}
bytesAllocated = myPis->sizeInBytes;
arrayInfos.addArray(layerNumber, arrayInfoStruct::arrayType_plyInfos, myPis->sizeInBytes, 0);
myPis->plyInfoIsLoaded = true;
memoryUsed2 += bytesAllocated;
PRINT(3, this, "Allocated " << bytesAllocated << " bytes in memory for ply info of layer " << layerNumber << ", which is " << (myPis->plyInfoIsCompletedAndInFile?"":" NOT ") << " fully calculated, due to read operation.");
}
LeaveCriticalSection(&csDatabase);
}
// measure io-operations per second
LARGE_INTEGER curTimeBefore;
if (MEASURE_IOPS && MEASURE_ONLY_IO) {
QueryPerformanceCounter(&curTimeBefore);
}
// read ply info from array
value = myPis->plyInfo[stateNumber];
// measure io-operations per second
measureIops(numReadPlyOperations, readPlyInterval, curTimeBefore, "Read ply info ");
}
}
//-----------------------------------------------------------------------------
// Name: saveKnotValueInDatabase()
// Desc:
//-----------------------------------------------------------------------------
void miniMax::saveKnotValueInDatabase(unsigned int layerNumber, unsigned int stateNumber, twoBit knotValue)
{
// locals
long long bytesAllocated;
twoBit defValue = SKV_WHOLE_BYTE_IS_INVALID;
layerStatsStruct * myLss = &layerStats[layerNumber];
// valid state and layer number ?
if (layerNumber > skvfHeader.numLayers || stateNumber > myLss->knotsInLayer) {
PRINT(0, this, "ERROR: INVALID layerNumber OR stateNumber in saveKnotValueInDatabase()!");
return;
}
// is layer already completed ?
if (myLss->layerIsCompletedAndInFile) {
PRINT(0, this, "ERROR: layer already completed and in file! function: saveKnotValueInDatabase()!");
return;
}
// is layer already loaded?
if (!myLss->layerIsLoaded) {
EnterCriticalSection(&csDatabase);
if (!myLss->layerIsLoaded) {
// reserve memory for this layer & create array for ply info with default value
myLss->shortKnotValueByte = new twoBit[myLss->sizeInBytes];
memset(myLss->shortKnotValueByte, SKV_WHOLE_BYTE_IS_INVALID, myLss->sizeInBytes);
bytesAllocated = myLss->sizeInBytes;
arrayInfos.addArray(layerNumber, arrayInfoStruct::arrayType_layerStats, myLss->sizeInBytes, 0);
// output
memoryUsed2 += bytesAllocated;
PRINT(3, this, "Allocated " << bytesAllocated << " bytes in memory for knot values of layer " << layerNumber << " due to write operation!");
myLss->layerIsLoaded = true;
}
LeaveCriticalSection(&csDatabase);
}
// measure io-operations per second
LARGE_INTEGER curTimeBefore;
if (MEASURE_IOPS && MEASURE_ONLY_IO) {
QueryPerformanceCounter(&curTimeBefore);
}
// set value
long * pShortKnotValue = ((long*) myLss->shortKnotValueByte) + stateNumber / ((sizeof(long)*8) / 2);
long numBitsToShift = 2 * (stateNumber % ((sizeof(long)*8) / 2)); // little-endian byte-order
long mask = 0x00000003 << numBitsToShift;
long curShortKnotValueLong, newShortKnotValueLong;
do {
curShortKnotValueLong = *pShortKnotValue;
newShortKnotValueLong = (curShortKnotValueLong & (~mask)) + (knotValue << numBitsToShift);
} while (InterlockedCompareExchange(pShortKnotValue, newShortKnotValueLong, curShortKnotValueLong) != curShortKnotValueLong);
// measure io-operations per second
measureIops(numWriteSkvOperations, writeSkvInterval, curTimeBefore, "Write knot value ");
}
//-----------------------------------------------------------------------------
// Name: savePlyInfoInDatabase()
// Desc:
//-----------------------------------------------------------------------------
void miniMax::savePlyInfoInDatabase(unsigned int layerNumber, unsigned int stateNumber, plyInfoVarType value)
{
// locals
unsigned int curKnot;
plyInfoVarType defValue = PLYINFO_VALUE_UNCALCULATED;
long long bytesAllocated;
plyInfoStruct * myPis = &plyInfos[layerNumber];
// valid state and layer number ?
if (layerNumber > plyInfoHeader.numLayers || stateNumber > myPis->knotsInLayer) {
PRINT(0, this, "ERROR: INVALID layerNumber OR stateNumber in savePlyInfoInDatabase()!");
return;
}
// is layer already completed ?
if (myPis->plyInfoIsCompletedAndInFile) {
PRINT(0, this, "ERROR: layer already completed and in file! function: savePlyInfoInDatabase()!");
return;
}
// is layer already loaded
if (!myPis->plyInfoIsLoaded) {
EnterCriticalSection(&csDatabase);
if (!myPis->plyInfoIsLoaded) {
// reserve memory for this layer & create array for ply info with default value
myPis->plyInfo = new plyInfoVarType[myPis->knotsInLayer];
for (curKnot=0; curKnot<myPis->knotsInLayer; curKnot++) { myPis->plyInfo[curKnot] = defValue; }
bytesAllocated = myPis->sizeInBytes;
arrayInfos.addArray(layerNumber, arrayInfoStruct::arrayType_plyInfos, myPis->sizeInBytes, 0);
myPis->plyInfoIsLoaded = true;
memoryUsed2 += bytesAllocated;
PRINT(3, this, "Allocated " << bytesAllocated << " bytes in memory for ply info of layer " << layerNumber << " due to write operation!");
}
LeaveCriticalSection(&csDatabase);
}
// measure io-operations per second
LARGE_INTEGER curTimeBefore;
if (MEASURE_IOPS && MEASURE_ONLY_IO) {
QueryPerformanceCounter(&curTimeBefore);
}
// set value
myPis->plyInfo[stateNumber] = value;
// measure io-operations per second
measureIops(numWritePlyOperations, writePlyInterval, curTimeBefore, "Write ply info ");
}

View File

@ -0,0 +1,749 @@
/*********************************************************************
miniMax_retroAnalysis.cpp
Copyright (c) Thomas Weber. All rights reserved.
Licensed under the MIT License.
https://github.com/madweasel/madweasels-cpp
s\*********************************************************************/
#include "miniMax.h"
//-----------------------------------------------------------------------------
// Name: calcKnotValuesByRetroAnalysis()
// Desc:
// The COUNT-ARRAY is the main element of the algorithmn. It contains the number of succeding states for the drawn gamestates,
// whose short knot value has to be determined. If all succeding states (branches representing possible moves) are for example won than,
// a state can be marked as lost, since no branch will lead to a drawn or won situation any more.
// Each time the short knot value of a game state has been determined, the state will be added to 'statesToProcess'.
// This list is like a queue of states, which still has to be processed.
//-----------------------------------------------------------------------------
bool miniMax::calcKnotValuesByRetroAnalysis(vector<unsigned int> &layersToCalculate)
{
// locals
bool abortCalculation = false;
unsigned int curLayer = 0; // Counter variable
unsigned int curSubLayer = 0; // Counter variable
unsigned int plyCounter = 0; // Counter variable
unsigned int threadNo;
stringstream ssLayers;
retroAnalysisGlobalVars retroVars;
// init retro vars
retroVars.thread.resize(threadManager.getNumThreads());
for (threadNo=0; threadNo<threadManager.getNumThreads(); threadNo++) {
retroVars.thread[threadNo].statesToProcess.resize(PLYINFO_EXP_VALUE, NULL);
retroVars.thread[threadNo].numStatesToProcess = 0;
retroVars.thread[threadNo].threadNo = threadNo;
}
retroVars.countArrays.resize(layersToCalculate.size(), NULL);
retroVars.layerInitialized.resize(skvfHeader.numLayers, false);
retroVars.layersToCalculate = layersToCalculate;
retroVars.pMiniMax = this;
for (retroVars.totalNumKnots=0, retroVars.numKnotsToCalc=0, curLayer=0; curLayer<layersToCalculate.size(); curLayer++) {
retroVars.numKnotsToCalc += layerStats[layersToCalculate[curLayer]].knotsInLayer;
retroVars.totalNumKnots += layerStats[layersToCalculate[curLayer]].knotsInLayer;
retroVars.layerInitialized[layersToCalculate[curLayer]] = true;
for (curSubLayer=0; curSubLayer<layerStats[layersToCalculate[curLayer]].numSuccLayers; curSubLayer++) {
if (retroVars.layerInitialized[layerStats[layersToCalculate[curLayer]].succLayers[curSubLayer]]) continue;
else retroVars.layerInitialized[layerStats[layersToCalculate[curLayer]].succLayers[curSubLayer]] = true;
retroVars.totalNumKnots += layerStats[layerStats[layersToCalculate[curLayer]].succLayers[curSubLayer]].knotsInLayer;
}
}
retroVars.layerInitialized.assign(skvfHeader.numLayers, false);
// output & filenames
for (curLayer=0; curLayer<layersToCalculate.size(); curLayer++) ssLayers << " " << layersToCalculate[curLayer];
PRINT(0, this, "*** Calculate layers" << ssLayers.str() << " by retro analysis ***");
// initialization
PRINT(2, this, " Bytes in memory: " << memoryUsed2 << endl);
if (!initRetroAnalysis(retroVars)) { abortCalculation = true; goto freeMem; }
// prepare count arrays
PRINT(2, this, " Bytes in memory: " << memoryUsed2 << endl);
if (!prepareCountArrays(retroVars)) { abortCalculation = true; goto freeMem; }
// stop here if only preparing layer
if (onlyPrepareLayer) goto freeMem;
// iteration
PRINT(2, this, " Bytes in memory: " << memoryUsed2 << endl);
if (!performRetroAnalysis(retroVars)) { abortCalculation = true; goto freeMem; }
// show output
PRINT(2, this, " Bytes in memory: " << memoryUsed2);
for (curLayer=0; curLayer<layersToCalculate.size(); curLayer++) { showLayerStats(layersToCalculate[curLayer]); }
PRINT(2, this, "");
// free memory
freeMem:
for (threadNo=0; threadNo<threadManager.getNumThreads(); threadNo++) {
for (plyCounter=0; plyCounter<retroVars.thread[threadNo].statesToProcess.size(); plyCounter++) {
SAFE_DELETE(retroVars.thread[threadNo].statesToProcess[plyCounter]);
}
}
for (curLayer=0; curLayer<layersToCalculate.size(); curLayer++) {
if (retroVars.countArrays[curLayer] != NULL) {
memoryUsed2 -= layerStats[layersToCalculate[curLayer]].knotsInLayer * sizeof(countArrayVarType);
arrayInfos.removeArray(layersToCalculate[curLayer], arrayInfoStruct::arrayType_countArray, layerStats[layersToCalculate[curLayer]].knotsInLayer * sizeof(countArrayVarType), 0);
}
SAFE_DELETE_ARRAY(retroVars.countArrays[curLayer]);
}
if (!abortCalculation) PRINT(2, this, " Bytes in memory: " << memoryUsed2);
return !abortCalculation;
}
//-----------------------------------------------------------------------------
// Name: initRetroAnalysis()
// Desc: The state values for all game situations in the database are marked as invalid, as undecided, as won or as lost by using the function getValueOfSituation().
//-----------------------------------------------------------------------------
bool miniMax::initRetroAnalysis(retroAnalysisGlobalVars &retroVars)
{
// locals
unsigned int curLayerId; // current processed layer within 'layersToCalculate'
unsigned int layerNumber; // layer number of the current process layer
stringstream ssInitArrayPath; // path of the working directory
stringstream ssInitArrayFilePath; // filename corresponding to a cyclic array file which is used for storage
bufferedFileClass* initArray; //
bool initAlreadyDone = false; // true if the initialization information is already available in a file
// process each layer
for (curLayerId=0; curLayerId<retroVars.layersToCalculate.size(); curLayerId++) {
// set current processed layer number
layerNumber = retroVars.layersToCalculate[curLayerId];
curCalculationActionId = MM_ACTION_INIT_RETRO_ANAL;
PRINT(1, this, endl << " *** Initialization of layer " << layerNumber << " (" << (getOutputInformation(layerNumber)) << ") which has " << layerStats[layerNumber].knotsInLayer << " knots ***");
// file names
ssInitArrayPath.str(""); ssInitArrayPath << fileDirectory << (fileDirectory.size()?"\\":"") << "initLayer";
ssInitArrayFilePath.str(""); ssInitArrayFilePath << fileDirectory << (fileDirectory.size()?"\\":"") << "initLayer\\initLayer" << layerNumber << ".dat";
// does initialization file exist ?
CreateDirectoryA(ssInitArrayPath.str().c_str(), NULL);
initArray = new bufferedFileClass(threadManager.getNumThreads(), FILE_BUFFER_SIZE, ssInitArrayFilePath.str().c_str());
if (initArray->getFileSize() == (LONGLONG) layerStats[layerNumber].knotsInLayer) {
PRINT(2, this, " Loading init states from file: " << ssInitArrayFilePath.str());
initAlreadyDone = true;
}
// don't add layers twice
if (retroVars.layerInitialized[layerNumber]) continue;
else retroVars.layerInitialized[layerNumber] = true;
// prepare parameters
numStatesProcessed = 0;
retroVars.statsValueCounter[SKV_VALUE_GAME_WON ] = 0;
retroVars.statsValueCounter[SKV_VALUE_GAME_LOST ] = 0;
retroVars.statsValueCounter[SKV_VALUE_GAME_DRAWN] = 0;
retroVars.statsValueCounter[SKV_VALUE_INVALID ] = 0;
threadManagerClass::threadVarsArray<initRetroAnalysisVars> tva(threadManager.getNumThreads(), initRetroAnalysisVars(this, &retroVars, layerNumber, initArray, initAlreadyDone));
// process each state in the current layer
switch (threadManager.executeParallelLoop(initRetroAnalysisThreadProc, tva.getPointerToArray(), tva.getSizeOfArray(), TM_SCHEDULE_STATIC, 0, layerStats[layerNumber].knotsInLayer - 1, 1))
{
case TM_RETURN_VALUE_OK:
break;
case TM_RETURN_VALUE_EXECUTION_CANCELLED:
PRINT(0,this, "\n****************************************\nMain thread: Execution cancelled by user!\n****************************************\n");
SAFE_DELETE(initArray);
return false;
default:
case TM_RETURN_VALUE_INVALID_PARAM:
case TM_RETURN_VALUE_UNEXPECTED_ERROR:
return falseOrStop();
}
// reduce and delete thread specific data
tva.reduce();
initAlreadyDone = false;
initArray->flushBuffers();
SAFE_DELETE(initArray);
if (numStatesProcessed < layerStats[layerNumber].knotsInLayer) return falseOrStop();
// when init file was created new then save it now
PRINT(2, this, " Saved initialized states to file: " << ssInitArrayFilePath.str());
// show statistics
PRINT(2, this, " won states: " << retroVars.statsValueCounter[SKV_VALUE_GAME_WON ]);
PRINT(2, this, " lost states: " << retroVars.statsValueCounter[SKV_VALUE_GAME_LOST ]);
PRINT(2, this, " draw states: " << retroVars.statsValueCounter[SKV_VALUE_GAME_DRAWN]);
PRINT(2, this, " invalid states: " << retroVars.statsValueCounter[SKV_VALUE_INVALID ]);
}
return true;
}
//-----------------------------------------------------------------------------
// Name: initRetroAnalysisParallelSub()
// Desc:
//-----------------------------------------------------------------------------
DWORD miniMax::initRetroAnalysisThreadProc(void* pParameter, int index)
{
// locals
initRetroAnalysisVars * iraVars = (initRetroAnalysisVars *) pParameter;
miniMax * m = iraVars->pMiniMax;
float floatValue; // dummy variable for calls of getValueOfSituation()
stateAdressStruct curState; // current state counter for loops
twoBit curStateValue; // for calls of getValueOfSituation()
curState.layerNumber = iraVars->layerNumber;
curState.stateNumber = index;
iraVars->statesProcessed++;
// print status
if (iraVars->statesProcessed % OUTPUT_EVERY_N_STATES == 0) {
m->numStatesProcessed += OUTPUT_EVERY_N_STATES;
PRINT(2, m, "Already initialized " << m->numStatesProcessed << " of " << m->layerStats[curState.layerNumber].knotsInLayer << " states");
}
// layer initialization already done ? if so, then read from file
if (iraVars->initAlreadyDone) {
if (!iraVars->bufferedFile->readBytes(iraVars->curThreadNo, index * sizeof(twoBit), sizeof(twoBit), (unsigned char*) &curStateValue)) {
PRINT(0, m, "ERROR: initArray->takeBytes() failed");
return m->falseOrStop();
}
// initialization not done
} else {
// set current selected situation
if (!m->setSituation(iraVars->curThreadNo, curState.layerNumber, curState.stateNumber)) {
curStateValue = SKV_VALUE_INVALID;
} else {
// get value of current situation
m->getValueOfSituation(iraVars->curThreadNo, floatValue, curStateValue);
}
}
// save init value
if (curStateValue != SKV_VALUE_INVALID) {
// save short knot value
m->saveKnotValueInDatabase(curState.layerNumber, curState.stateNumber, curStateValue);
// put in list if state is final
if (curStateValue == SKV_VALUE_GAME_WON || curStateValue == SKV_VALUE_GAME_LOST) {
// ply info
m->savePlyInfoInDatabase(curState.layerNumber, curState.stateNumber, 0);
// add state to list
m->addStateToProcessQueue(*iraVars->retroVars, iraVars->retroVars->thread[iraVars->curThreadNo], 0, &curState);
}
}
// write data to file
if (!iraVars->initAlreadyDone) {
// curStateValue sollte 2 sein bei index == 1329322
if (!iraVars->bufferedFile->writeBytes(iraVars->curThreadNo, index * sizeof(twoBit), sizeof(twoBit), (unsigned char*) &curStateValue)) {
PRINT(0, m, "ERROR: bufferedFile->writeBytes failed!");
return m->falseOrStop();
}
}
iraVars->statsValueCounter[curStateValue]++;
return TM_RETURN_VALUE_OK;
}
//-----------------------------------------------------------------------------
// Name: prepareCountArrays()
// Desc:
//-----------------------------------------------------------------------------
bool miniMax::prepareCountArrays(retroAnalysisGlobalVars &retroVars)
{
// locals
unsigned int numKnotsInCurLayer;
stateAdressStruct curState; // current state counter for loops
unsigned int curLayer = 0; // Counter variable
countArrayVarType defValue = 0; // default counter array value
DWORD dwWritten;
DWORD dwRead;
LARGE_INTEGER fileSize;
HANDLE hFileCountArray = NULL; // file handle for loading and saving the arrays in 'countArrays'
stringstream ssCountArrayPath;
stringstream ssCountArrayFilePath;
stringstream ssLayers;
// output & filenames
for (curLayer=0; curLayer<retroVars.layersToCalculate.size(); curLayer++) ssLayers << " " << retroVars.layersToCalculate[curLayer];
ssCountArrayPath << fileDirectory << (fileDirectory.size()?"\\":"") << "countArray";
ssCountArrayFilePath << fileDirectory << (fileDirectory.size()?"\\":"") << "countArray\\countArray" << ssLayers.str() << ".dat";
PRINT(2, this, " *** Prepare count arrays for layers " << ssLayers.str() << " ***" << endl);
curCalculationActionId = MM_ACTION_PREPARE_COUNT_ARRAY;
// prepare count arrays
CreateDirectoryA(ssCountArrayPath.str().c_str(), NULL);
if ((hFileCountArray = CreateFileA(ssCountArrayFilePath.str().c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE) {
PRINT(0, this, "ERROR: Could not open File " << ssCountArrayFilePath.str() << "!");
return falseOrStop();
}
// allocate memory for count arrays
for (curLayer=0; curLayer<retroVars.layersToCalculate.size(); curLayer++) {
numKnotsInCurLayer = layerStats[retroVars.layersToCalculate[curLayer]].knotsInLayer;
retroVars.countArrays[curLayer] = new countArrayVarType[numKnotsInCurLayer];
memoryUsed2 += numKnotsInCurLayer * sizeof(countArrayVarType);
arrayInfos.addArray(retroVars.layersToCalculate[curLayer], arrayInfoStruct::arrayType_countArray, numKnotsInCurLayer * sizeof(countArrayVarType), 0);
}
// load file if already existend
if (GetFileSizeEx(hFileCountArray, &fileSize) && fileSize.QuadPart == retroVars.numKnotsToCalc) {
PRINT(2, this, " Load number of succedors from file: " << ssCountArrayFilePath.str().c_str());
for (curLayer=0; curLayer<retroVars.layersToCalculate.size(); curLayer++) {
numKnotsInCurLayer = layerStats[retroVars.layersToCalculate[curLayer]].knotsInLayer;
if (!ReadFile(hFileCountArray, retroVars.countArrays[curLayer], numKnotsInCurLayer * sizeof(countArrayVarType), &dwRead, NULL)) return falseOrStop();
if (dwRead != numKnotsInCurLayer * sizeof(countArrayVarType)) return falseOrStop();
}
// else calculate number of succedding states
} else {
// Set default value 0
for (curLayer=0; curLayer<retroVars.layersToCalculate.size(); curLayer++) {
numKnotsInCurLayer = layerStats[retroVars.layersToCalculate[curLayer]].knotsInLayer;
for (curState.stateNumber=0; curState.stateNumber<numKnotsInCurLayer; curState.stateNumber++) {
retroVars.countArrays[curLayer][curState.stateNumber] = defValue;
}
}
// calc values
if (!calcNumSuccedors(retroVars)) {
CloseHandle(hFileCountArray);
return false;
}
// save to file
for (curLayer=0, dwWritten=0; curLayer<retroVars.layersToCalculate.size(); curLayer++) {
numKnotsInCurLayer = layerStats[retroVars.layersToCalculate[curLayer]].knotsInLayer;
if (!WriteFile(hFileCountArray, retroVars.countArrays[curLayer], numKnotsInCurLayer * sizeof(countArrayVarType), &dwWritten, NULL)) return falseOrStop();
if (dwWritten != numKnotsInCurLayer * sizeof(countArrayVarType)) return falseOrStop();
}
PRINT(2, this, " Count array saved to file: " << ssCountArrayFilePath.str());
}
// finish
CloseHandle(hFileCountArray);
return true;
}
//-----------------------------------------------------------------------------
// Name: calcNumSuccedors()
// Desc:
//-----------------------------------------------------------------------------
bool miniMax::calcNumSuccedors(retroAnalysisGlobalVars &retroVars)
{
// locals
unsigned int curLayerId; // current processed layer within 'layersToCalculate'
unsigned int layerNumber; // layer number of the current process layer
stateAdressStruct curState; // current state counter for loops
stateAdressStruct succState; // current succeding state counter for loops
vector<bool> succCalculated(skvfHeader.numLayers, false); //
// process each layer
for (curLayerId=0; curLayerId<retroVars.layersToCalculate.size(); curLayerId++) {
// set current processed layer number
layerNumber = retroVars.layersToCalculate[curLayerId];
PRINT(0, this, " *** Calculate number of succeding states for each state of layer " << layerNumber << " ***");
// process layer ...
if (!succCalculated[layerNumber]) {
// prepare parameters for multithreading
succCalculated[layerNumber] = true;
numStatesProcessed = 0;
threadManagerClass::threadVarsArray<addNumSuccedorsVars> tva(threadManager.getNumThreads(), addNumSuccedorsVars(this, &retroVars, layerNumber));
// process each state in the current layer
switch (threadManager.executeParallelLoop(addNumSuccedorsThreadProc, tva.getPointerToArray(), tva.getSizeOfArray(), TM_SCHEDULE_STATIC, 0, layerStats[layerNumber].knotsInLayer - 1, 1))
{
case TM_RETURN_VALUE_OK:
break;
case TM_RETURN_VALUE_EXECUTION_CANCELLED:
PRINT(0,this, "\n****************************************\nMain thread: Execution cancelled by user!\n****************************************\n");
return false;
default:
case TM_RETURN_VALUE_INVALID_PARAM:
case TM_RETURN_VALUE_UNEXPECTED_ERROR:
return falseOrStop();
}
// reduce and delete thread specific data
tva.reduce();
if (numStatesProcessed < layerStats[layerNumber].knotsInLayer) return falseOrStop();
// don't calc layers twice
} else {
return falseOrStop();
}
// ... and process succeding layers
for (curState.layerNumber=0; curState.layerNumber<layerStats[layerNumber].numSuccLayers; curState.layerNumber++) {
// get current pred. layer
succState.layerNumber = layerStats[layerNumber].succLayers[curState.layerNumber];
// don't add layers twice
if (succCalculated[succState.layerNumber]) continue;
else succCalculated[succState.layerNumber] = true;
// don't process layers without states
if (!layerStats[succState.layerNumber].knotsInLayer) continue;
// check all states of pred. layer
PRINT(2, this, " - Do the same for the succeding layer " << (int) succState.layerNumber);
// prepare parameters for multithreading
numStatesProcessed = 0;
threadManagerClass::threadVarsArray<addNumSuccedorsVars> tva(threadManager.getNumThreads(), addNumSuccedorsVars(this, &retroVars, succState.layerNumber));
// process each state in the current layer
switch (threadManager.executeParallelLoop(addNumSuccedorsThreadProc, tva.getPointerToArray(), tva.getSizeOfArray(), TM_SCHEDULE_STATIC, 0, layerStats[succState.layerNumber].knotsInLayer - 1, 1))
{
case TM_RETURN_VALUE_OK:
break;
case TM_RETURN_VALUE_EXECUTION_CANCELLED:
PRINT(0,this, "\n****************************************\nMain thread: Execution cancelled by user!\n****************************************\n");
return false;
default:
case TM_RETURN_VALUE_INVALID_PARAM:
case TM_RETURN_VALUE_UNEXPECTED_ERROR:
return falseOrStop();
}
// reduce and delete thread specific data
tva.reduce();
if (numStatesProcessed < layerStats[succState.layerNumber].knotsInLayer) return falseOrStop();
}
}
// everything fine
return true;
}
//-----------------------------------------------------------------------------
// Name: addNumSuccedorsThreadProc()
// Desc:
//-----------------------------------------------------------------------------
DWORD miniMax::addNumSuccedorsThreadProc(void* pParameter, int index)
{
// locals
addNumSuccedorsVars * ansVars = (addNumSuccedorsVars *) pParameter;
miniMax * m = ansVars->pMiniMax;
unsigned int numLayersToCalculate = (unsigned int) ansVars->retroVars->layersToCalculate.size();
unsigned int curLayerId; // current processed layer within 'layersToCalculate'
unsigned int amountOfPred;
unsigned int curPred;
countArrayVarType countValue;
stateAdressStruct predState;
stateAdressStruct curState;
twoBit curStateValue;
plyInfoVarType numPlies; // number of plies of the current considered succeding state
bool cuStateAddedToProcessQueue = false;
curState.layerNumber = ansVars->layerNumber;
curState.stateNumber = (stateNumberVarType) index;
// print status
ansVars->statesProcessed++;
if (ansVars->statesProcessed % OUTPUT_EVERY_N_STATES == 0) {
m->numStatesProcessed += OUTPUT_EVERY_N_STATES;
PRINT(2, m, " Already processed " << m->numStatesProcessed << " of " << m->layerStats[curState.layerNumber].knotsInLayer << " states");
}
// invalid state ?
m->readKnotValueFromDatabase(curState.layerNumber, curState.stateNumber, curStateValue);
if (curStateValue == SKV_VALUE_INVALID) return TM_RETURN_VALUE_OK;
// set current selected situation
if (!m->setSituation(ansVars->curThreadNo, curState.layerNumber, curState.stateNumber)) {
PRINT(0, m, "ERROR: setSituation() returned false!");
return TM_RETURN_VALUE_TERMINATE_ALL_THREADS;
}
// get list with statenumbers of predecessors
m->getPredecessors(ansVars->curThreadNo, &amountOfPred, ansVars->predVars);
// iteration
for (curPred=0; curPred<amountOfPred; curPred++) {
// current predecessor
predState.layerNumber = ansVars->predVars[curPred].predLayerNumbers;
predState.stateNumber = ansVars->predVars[curPred].predStateNumbers;
// don't calculate states from layers above yet
for (curLayerId=0; curLayerId<numLayersToCalculate; curLayerId++) {
if (ansVars->retroVars->layersToCalculate[curLayerId] == predState.layerNumber) break;
}
if (curLayerId == numLayersToCalculate) continue;
// put in list (with states to be processed) if state is final
if (!cuStateAddedToProcessQueue && (curStateValue == SKV_VALUE_GAME_WON || curStateValue == SKV_VALUE_GAME_LOST)) {
m->readPlyInfoFromDatabase(curState.layerNumber, curState.stateNumber, numPlies);
m->addStateToProcessQueue(*ansVars->retroVars, ansVars->retroVars->thread[ansVars->curThreadNo], numPlies, &curState);
cuStateAddedToProcessQueue = true;
}
// add this state as possible move
long * pCountValue = ((long*) ansVars->retroVars->countArrays[curLayerId]) + predState.stateNumber / (sizeof(long) / sizeof(countArrayVarType));
long numBitsToShift = sizeof(countArrayVarType) * 8 * (predState.stateNumber % (sizeof(long) / sizeof(countArrayVarType))); // little-endian byte-order
long mask = 0x000000ff << numBitsToShift;
long curCountLong, newCountLong;
do {
curCountLong = *pCountValue;
countValue = (countArrayVarType) ((curCountLong & mask) >> numBitsToShift);
if (countValue == 255) {
PRINT(0, m, "ERROR: maximum value for Count[] reached!");
return TM_RETURN_VALUE_TERMINATE_ALL_THREADS;
} else {
countValue++;
newCountLong = (curCountLong & (~mask)) + (countValue << numBitsToShift);
}
} while (InterlockedCompareExchange(pCountValue, newCountLong, curCountLong) != curCountLong);
}
// everything is fine
return TM_RETURN_VALUE_OK;
}
//-----------------------------------------------------------------------------
// Name: performRetroAnalysis()
// Desc:
//-----------------------------------------------------------------------------
bool miniMax::performRetroAnalysis(retroAnalysisGlobalVars &retroVars)
{
// locals
stateAdressStruct curState; // current state counter for loops
twoBit curStateValue; // current state value
unsigned int curLayerId; // current processed layer within 'layersToCalculate'
PRINT(2, this, " *** Begin Iteration ***");
numStatesProcessed = 0;
curCalculationActionId = MM_ACTION_PERFORM_RETRO_ANAL;
// process each state in the current layer
switch (threadManager.executeInParallel(performRetroAnalysisThreadProc, (void**) &retroVars, 0))
{
case TM_RETURN_VALUE_OK:
break;
case TM_RETURN_VALUE_EXECUTION_CANCELLED:
PRINT(0,this, "\n****************************************\nMain thread: Execution cancelled by user!\n****************************************\n");
return false;
default:
case TM_RETURN_VALUE_INVALID_PARAM:
case TM_RETURN_VALUE_UNEXPECTED_ERROR:
return falseOrStop();
}
// if there are still states to process, than something went wrong
for (unsigned int curThreadNo=0; curThreadNo<threadManager.getNumThreads(); curThreadNo++) {
if (retroVars.thread[curThreadNo].numStatesToProcess) {
PRINT(0, this, "ERROR: There are still states to process after performing retro analysis!");
return falseOrStop();
}
}
// copy drawn and invalid states to ply info
PRINT(2, this, " Copy drawn and invalid states to ply info database...");
for (curLayerId=0; curLayerId<retroVars.layersToCalculate.size(); curLayerId++) {
for (curState.layerNumber=retroVars.layersToCalculate[curLayerId], curState.stateNumber=0; curState.stateNumber<layerStats[curState.layerNumber].knotsInLayer; curState.stateNumber++) {
readKnotValueFromDatabase(curState.layerNumber, curState.stateNumber, curStateValue);
if (curStateValue == SKV_VALUE_GAME_DRAWN) savePlyInfoInDatabase(curState.layerNumber, curState.stateNumber, PLYINFO_VALUE_DRAWN);
if (curStateValue == SKV_VALUE_INVALID) savePlyInfoInDatabase(curState.layerNumber, curState.stateNumber, PLYINFO_VALUE_INVALID);
}
}
PRINT(1, this, " *** Iteration finished! ***");
// every thing ok
return true;
}
//-----------------------------------------------------------------------------
// Name: performRetroAnalysisThreadProc()
// Desc:
//-----------------------------------------------------------------------------
DWORD miniMax::performRetroAnalysisThreadProc(void* pParameter)
{
// locals
retroAnalysisGlobalVars * retroVars = (retroAnalysisGlobalVars *) pParameter;
miniMax * m = retroVars->pMiniMax;
unsigned int threadNo = m->threadManager.getThreadNumber();
retroAnalysisThreadVars * threadVars = &retroVars->thread[threadNo];
twoBit predStateValue;
unsigned int curLayerId; // current processed layer within 'layersToCalculate'
unsigned int amountOfPred; // total numbers of predecessors and current considered one
unsigned int curPred;
unsigned int threadCounter;
long long numStatesProcessed;
long long totalNumStatesToProcess;
plyInfoVarType curNumPlies;
plyInfoVarType numPliesTillCurState;
plyInfoVarType numPliesTillPredState;
countArrayVarType countValue;
stateAdressStruct predState;
stateAdressStruct curState; // current state counter for while-loop
twoBit curStateValue; // current state value
retroAnalysisPredVars predVars[MAX_NUM_PREDECESSORS];
for (numStatesProcessed = 0, curNumPlies=0; curNumPlies<threadVars->statesToProcess.size(); curNumPlies++) {
// skip empty and uninitialized cyclic arrays
if (threadVars->statesToProcess[curNumPlies] != NULL) {
if (threadNo==0) {
PRINT(0, m, " Current number of plies: " << (unsigned int) curNumPlies << "/" << threadVars->statesToProcess.size());
for (threadCounter=0; threadCounter<m->threadManager.getNumThreads(); threadCounter++) {
PRINT(0, m, " States to process for thread " << threadCounter << ": " << retroVars->thread[threadCounter].numStatesToProcess);
}
}
while (threadVars->statesToProcess[curNumPlies]->takeBytes(sizeof(stateAdressStruct), (unsigned char*) &curState)) {
// execution cancelled by user?
if (m->threadManager.wasExecutionCancelled()) {
PRINT(0,m, "\n****************************************\nSub-thread no. " << threadNo << ": Execution cancelled by user!\n****************************************\n");
return TM_RETURN_VALUE_EXECUTION_CANCELLED;
}
// get value of current state
m->readKnotValueFromDatabase(curState.layerNumber, curState.stateNumber, curStateValue);
m->readPlyInfoFromDatabase (curState.layerNumber, curState.stateNumber, numPliesTillCurState);
if (numPliesTillCurState != curNumPlies) {
PRINT(0,m,"ERROR: numPliesTillCurState != curNumPlies");
return TM_RETURN_VALUE_TERMINATE_ALL_THREADS;
}
// console output
numStatesProcessed++;
threadVars->numStatesToProcess--;
if (numStatesProcessed % OUTPUT_EVERY_N_STATES == 0) {
m->numStatesProcessed += OUTPUT_EVERY_N_STATES;
for (totalNumStatesToProcess=0, threadCounter=0; threadCounter<m->threadManager.getNumThreads(); threadCounter++) {
totalNumStatesToProcess += retroVars->thread[threadCounter].numStatesToProcess;
}
PRINT(2, m, " states already processed: " << m->numStatesProcessed << " \t states still in list: " << totalNumStatesToProcess);
}
// set current selected situation
if (!m->setSituation(threadNo, curState.layerNumber, curState.stateNumber)) {
PRINT(0,m,"ERROR: setSituation() returned false!");
return TM_RETURN_VALUE_TERMINATE_ALL_THREADS;
}
// get list with statenumbers of predecessors
m->getPredecessors(threadNo, &amountOfPred, predVars);
// iteration
for (curPred=0; curPred<amountOfPred; curPred++) {
// current predecessor
predState.layerNumber = predVars[curPred].predLayerNumbers;
predState.stateNumber = predVars[curPred].predStateNumbers;
// don't calculate states from layers above yet
for (curLayerId=0; curLayerId<retroVars->layersToCalculate.size(); curLayerId++) {
if (retroVars->layersToCalculate[curLayerId] == predState.layerNumber) break;
}
if (curLayerId == retroVars->layersToCalculate.size()) continue;
// get value of predecessor
m->readKnotValueFromDatabase(predState.layerNumber, predState.stateNumber, predStateValue);
// only drawn states are relevant here, since the other are already calculated
if (predStateValue == SKV_VALUE_GAME_DRAWN) {
// if current considered state is a lost game then all predecessors are a won game
if (curStateValue == m->skvPerspectiveMatrix[SKV_VALUE_GAME_LOST][predVars[curPred].playerToMoveChanged ? PL_TO_MOVE_CHANGED : PL_TO_MOVE_UNCHANGED]) {
m->saveKnotValueInDatabase(predState.layerNumber, predState.stateNumber, SKV_VALUE_GAME_WON);
m->savePlyInfoInDatabase (predState.layerNumber, predState.stateNumber, numPliesTillCurState + 1); // (requirement: curNumPlies == numPliesTillCurState)
if (numPliesTillCurState + 1 < curNumPlies) {
PRINT(0,m,"ERROR: Current number of plies is bigger than numPliesTillCurState + 1!");
return TM_RETURN_VALUE_TERMINATE_ALL_THREADS;
}
m->addStateToProcessQueue(*retroVars, *threadVars, numPliesTillCurState + 1, &predState);
// if current state is a won game, then this state is not an option any more for all predecessors
} else {
// reduce count value by one
long * pCountValue = ((long*) retroVars->countArrays[curLayerId]) + predState.stateNumber / (sizeof(long) / sizeof(countArrayVarType));
long numBitsToShift = sizeof(countArrayVarType) * 8 * (predState.stateNumber % (sizeof(long) / sizeof(countArrayVarType))); // little-endian byte-order
long mask = 0x000000ff << numBitsToShift;
long curCountLong, newCountLong;
do {
curCountLong = *pCountValue;
countValue = (countArrayVarType) ((curCountLong & mask) >> numBitsToShift);
if (countValue > 0) {
countValue--;
newCountLong = (curCountLong & (~mask)) + (countValue << numBitsToShift);
} else {
PRINT(0,m,"ERROR: Count is already zero!");
return TM_RETURN_VALUE_TERMINATE_ALL_THREADS;
}
} while (InterlockedCompareExchange(pCountValue, newCountLong, curCountLong) != curCountLong);
// ply info (requirement: curNumPlies == numPliesTillCurState)
m->readPlyInfoFromDatabase(predState.layerNumber, predState.stateNumber, numPliesTillPredState);
if (numPliesTillPredState == PLYINFO_VALUE_UNCALCULATED || numPliesTillCurState + 1 > numPliesTillPredState) {
m->savePlyInfoInDatabase(predState.layerNumber, predState.stateNumber, numPliesTillCurState + 1);
}
// when all successor are won states then this is a lost state (this should only be the case for one thread)
if (countValue == 0) {
m->saveKnotValueInDatabase(predState.layerNumber, predState.stateNumber, SKV_VALUE_GAME_LOST);
if (numPliesTillCurState + 1 < curNumPlies) {
PRINT(0,m,"ERROR: Current number of plies is bigger than numPliesTillCurState + 1!");
return TM_RETURN_VALUE_TERMINATE_ALL_THREADS;
}
m->addStateToProcessQueue(*retroVars, *threadVars, numPliesTillCurState + 1, &predState);
}
}
}
}
}
}
// there might be other threads still processing states with this ply number
m->threadManager.waitForOtherThreads(threadNo);
}
// every thing ok
return TM_RETURN_VALUE_OK;
}
//-----------------------------------------------------------------------------
// Name: addStateToProcessQueue()
// Desc:
//-----------------------------------------------------------------------------
bool miniMax::addStateToProcessQueue(retroAnalysisGlobalVars &retroVars, retroAnalysisThreadVars &threadVars, unsigned int plyNumber, stateAdressStruct* pState)
{
// resize vector if too small
if (plyNumber >= threadVars.statesToProcess.size()) {
threadVars.statesToProcess.resize(max(plyNumber+1, 10*threadVars.statesToProcess.size()), NULL);
PRINT(4, this, " statesToProcess resized to " << threadVars.statesToProcess.size());
}
// initialize cyclic array if necessary
if (threadVars.statesToProcess[plyNumber] == NULL) {
stringstream ssStatesToProcessFilePath;
stringstream ssStatesToProcessPath;
ssStatesToProcessPath << fileDirectory << (fileDirectory.size()?"\\":"") << "statesToProcess";
CreateDirectoryA(ssStatesToProcessPath.str().c_str(), NULL);
ssStatesToProcessFilePath.str("");
ssStatesToProcessFilePath << ssStatesToProcessPath.str() << "\\statesToProcessWithPlyCounter=" << plyNumber << "andThread=" << threadVars.threadNo << ".dat";
threadVars.statesToProcess[plyNumber] = new cyclicArray(BLOCK_SIZE_IN_CYCLIC_ARRAY * sizeof(stateAdressStruct), (unsigned int)(retroVars.totalNumKnots / BLOCK_SIZE_IN_CYCLIC_ARRAY) + 1, ssStatesToProcessFilePath.str().c_str());
PRINT(4, this, " Created cyclic array: " << ssStatesToProcessFilePath.str());
}
// add state
if (!threadVars.statesToProcess[plyNumber]->addBytes(sizeof(stateAdressStruct), (unsigned char*) pState)) {
PRINT(0, this, "ERROR: Cyclic list to small! numStatesToProcess:" << threadVars.numStatesToProcess);
return falseOrStop();
}
// everything was fine
threadVars.numStatesToProcess++;
return true;
}

View File

@ -0,0 +1,70 @@
/*********************************************************************\
strLib.h
Copyright (c) Thomas Weber. All rights reserved.
Licensed under the MIT License.
https://github.com/madweasel/madweasels-cpp
\*********************************************************************/
struct retroAnalysisQueueState
{
stateNumberVarType stateNumber; // state stored in the retro analysis queue. the queue is a buffer containing states to be passed to 'retroAnalysisThreadVars::statesToProcess'
plyInfoVarType numPliesTillCurState; // ply number for the stored state
};
struct retroAnalysisThreadVars // thread specific variables for each thread in the retro analysis
{
vector<cyclicArray*> statesToProcess; // vector-queue containing the states, whose short knot value are known for sure. they have to be processed. if processed the state will be removed from list. indexing: [threadNo][plyNumber]
vector<vector<retroAnalysisQueueState>> stateQueue; // Queue containing states, whose 'count value' shall be increased by one. Before writing 'count value' to 'count array' the writing positions are sorted for faster processing.
long long numStatesToProcess; // Number of states in 'statesToProcess' which have to be processed
unsigned int threadNo;
};
struct retroAnalysisVars // constant during calculation
{
vector<countArrayVarType *> countArrays; // One count array for each layer in 'layersToCalculate'. (For the nine men's morris game two layers have to considered at once.)
vector<compressorClass::compressedArrayClass *> countArraysCompr; // '' but compressed
vector<bool> layerInitialized; //
vector<unsigned int> layersToCalculate; // layers which shall be calculated
long long totalNumKnots; // total numbers of knots which have to be stored in memory
long long numKnotsToCalc; // number of knots of all layers to be calculated
vector<retroAnalysisThreadVars> thread;
};
struct initRetroAnalysisVars
{
miniMax * pMiniMax;
unsigned int curThreadNo;
unsigned int layerNumber;
LONGLONG statesProcessed;
unsigned int statsValueCounter[SKV_NUM_VALUES];
bufferedFileClass * bufferedFile;
retroAnalysisVars * retroVars;
bool initAlreadyDone; // true if the initialization information is already available in a file
};
struct addSuccLayersVars
{
miniMax * pMiniMax;
unsigned int curThreadNo;
unsigned int statsValueCounter[SKV_NUM_VALUES];
unsigned int layerNumber;
retroAnalysisVars * retroVars;
};
struct retroAnalysisPredVars
{
unsigned int predStateNumbers;
unsigned int predLayerNumbers;
unsigned int predSymOperation;
bool playerToMoveChanged;
};
struct addNumSuccedorsVars
{
miniMax * pMiniMax;
unsigned int curThreadNo;
unsigned int layerNumber;
LONGLONG statesProcessed;
retroAnalysisVars * retroVars;
retroAnalysisPredVars * predVars;
};

View File

@ -0,0 +1,410 @@
/*********************************************************************
miniMax_statistics.cpp
Copyright (c) Thomas Weber. All rights reserved.
Licensed under the MIT License.
https://github.com/madweasel/madweasels-cpp
\*********************************************************************/
#include "miniMax.h"
//-----------------------------------------------------------------------------
// Name: showMemoryStatus()
// Desc:
//-----------------------------------------------------------------------------
unsigned int miniMax::getNumThreads()
{
return threadManager.getNumThreads();
}
//-----------------------------------------------------------------------------
// Name: anyFreshlyCalculatedLayer()
// Desc: called by MAIN-thread in pMiniMax->csOsPrint critical-section
//-----------------------------------------------------------------------------
bool miniMax::anyFreshlyCalculatedLayer()
{
return (lastCalculatedLayer.size()>0);
}
//-----------------------------------------------------------------------------
// Name: getLastCalculatedLayer()
// Desc: called by MAIN-thread in pMiniMax->csOsPrint critical-section
//-----------------------------------------------------------------------------
unsigned int miniMax::getLastCalculatedLayer()
{
unsigned int tmp = lastCalculatedLayer.front();
lastCalculatedLayer.pop_front();
return tmp;
}
//-----------------------------------------------------------------------------
// Name: isLayerInDatabase()
// Desc:
//-----------------------------------------------------------------------------
bool miniMax::isLayerInDatabase(unsigned int layerNum)
{
if (layerStats == NULL) return false;
return layerStats[layerNum].layerIsCompletedAndInFile;
}
//-----------------------------------------------------------------------------
// Name: getLayerSizeInBytes()
// Desc:
//-----------------------------------------------------------------------------
long long miniMax::getLayerSizeInBytes(unsigned int layerNum)
{
if (plyInfos == NULL || layerStats == NULL) return 0;
return (long long) layerStats[layerNum].sizeInBytes + (long long) plyInfos[layerNum].sizeInBytes;
}
//-----------------------------------------------------------------------------
// Name: getNumWonStates()
// Desc:
//-----------------------------------------------------------------------------
miniMax::stateNumberVarType miniMax::getNumWonStates(unsigned int layerNum)
{
if (layerStats == NULL) return 0;
return layerStats[layerNum].numWonStates;
}
//-----------------------------------------------------------------------------
// Name: getNumLostStates()
// Desc:
//-----------------------------------------------------------------------------
miniMax::stateNumberVarType miniMax::getNumLostStates(unsigned int layerNum)
{
if (layerStats == NULL) return 0;
return layerStats[layerNum].numLostStates;
}
//-----------------------------------------------------------------------------
// Name: getNumDrawnStates()
// Desc:
//-----------------------------------------------------------------------------
miniMax::stateNumberVarType miniMax::getNumDrawnStates(unsigned int layerNum)
{
if (layerStats == NULL) return 0;
return layerStats[layerNum].numDrawnStates;
}
//-----------------------------------------------------------------------------
// Name: getNumInvalidStates()
// Desc:
//-----------------------------------------------------------------------------
miniMax::stateNumberVarType miniMax::getNumInvalidStates(unsigned int layerNum)
{
if (layerStats == NULL) return 0;
return layerStats[layerNum].numInvalidStates;
}
//-----------------------------------------------------------------------------
// Name: showMemoryStatus()
// Desc:
//-----------------------------------------------------------------------------
void miniMax::showMemoryStatus()
{
MEMORYSTATUSEX memStatus;
memStatus.dwLength = sizeof (memStatus);
GlobalMemoryStatusEx(&memStatus);
cout << endl << "dwMemoryLoad : " << memStatus.dwMemoryLoad;
cout << endl << "ullAvailExtendedVirtual: " << memStatus.ullAvailExtendedVirtual;
cout << endl << "ullAvailPageFile : " << memStatus.ullAvailPageFile;
cout << endl << "ullAvailPhys : " << memStatus.ullAvailPhys;
cout << endl << "ullAvailVirtual : " << memStatus.ullAvailVirtual;
cout << endl << "ullTotalPageFile : " << memStatus.ullTotalPageFile;
cout << endl << "ullTotalPhys : " << memStatus.ullTotalPhys;
cout << endl << "ullTotalVirtual : " << memStatus.ullTotalVirtual;
}
//-----------------------------------------------------------------------------
// Name: setOutputStream()
// Desc:
//-----------------------------------------------------------------------------
void miniMax::setOutputStream(ostream* theStream, void(*printFunc)(void *pUserData), void *pUserData)
{
osPrint = theStream;
pDataForUserPrintFunc = pUserData;
userPrintFunc = printFunc;
}
//-----------------------------------------------------------------------------
// Name: showLayerStats()
// Desc:
//-----------------------------------------------------------------------------
void miniMax::showLayerStats(unsigned int layerNumber)
{
// locals
stateAdressStruct curState;
unsigned int statsValueCounter[] = {0,0,0,0};
twoBit curStateValue;
// calc and show statistics
for (curState.layerNumber=layerNumber, curState.stateNumber=0; curState.stateNumber<layerStats[curState.layerNumber].knotsInLayer; curState.stateNumber++) {
// get state value
readKnotValueFromDatabase(curState.layerNumber, curState.stateNumber, curStateValue);
statsValueCounter[curStateValue]++;
}
layerStats[layerNumber].numWonStates = statsValueCounter[SKV_VALUE_GAME_WON ];
layerStats[layerNumber].numLostStates = statsValueCounter[SKV_VALUE_GAME_LOST ];
layerStats[layerNumber].numDrawnStates = statsValueCounter[SKV_VALUE_GAME_DRAWN];
layerStats[layerNumber].numInvalidStates = statsValueCounter[SKV_VALUE_INVALID ];
PRINT(1, this, endl << "FINAL STATISTICS OF LAYER " << layerNumber);
PRINT(1, this, (getOutputInformation(layerNumber)));
PRINT(1, this, " number states: " << layerStats[curState.layerNumber].knotsInLayer);
PRINT(1, this, " won states: " << statsValueCounter[SKV_VALUE_GAME_WON ]);
PRINT(1, this, " lost states: " << statsValueCounter[SKV_VALUE_GAME_LOST ]);
PRINT(1, this, " draw states: " << statsValueCounter[SKV_VALUE_GAME_DRAWN]);
PRINT(1, this, " invalid states: " << statsValueCounter[SKV_VALUE_INVALID ]);
}
//-----------------------------------------------------------------------------
// Name: calcLayerStatistics()
// Desc:
//-----------------------------------------------------------------------------
bool miniMax::calcLayerStatistics(char *statisticsFileName)
{
// locals
HANDLE statFile;
DWORD dwBytesWritten;
stateAdressStruct curState;
unsigned int *statsValueCounter;
twoBit curStateValue;
char line[10000];
string text("");
// database must be open
if (hFileShortKnotValues == NULL) return false;
// Open statistics file
statFile = CreateFileA(statisticsFileName, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
// opened file succesfully?
if (statFile == INVALID_HANDLE_VALUE) {
statFile = NULL;
return false;
}
// headline
text += "layer number\t";
text += "white stones\t";
text += "black stones\t";
text += "won states\t";
text += "lost states\t";
text += "draw states\t";
text += "invalid states\t";
text += "total num states\t";
text += "num succeding layers\t";
text += "partner layer\t";
text += "size in bytes\t";
text += "succLayers[0]\t";
text += "succLayers[1]\n";
statsValueCounter = new unsigned int[4 * skvfHeader.numLayers];
curCalculationActionId = MM_ACTION_CALC_LAYER_STATS;
// calc and show statistics
for (layerInDatabase=false, curState.layerNumber=0; curState.layerNumber<skvfHeader.numLayers; curState.layerNumber++) {
// status output
PRINT(0, this, "Calculating statistics of layer: " << (int) curState.layerNumber);
// zero counters
statsValueCounter[4*curState.layerNumber + SKV_VALUE_GAME_WON ] = 0;
statsValueCounter[4*curState.layerNumber + SKV_VALUE_GAME_LOST ] = 0;
statsValueCounter[4*curState.layerNumber + SKV_VALUE_GAME_DRAWN] = 0;
statsValueCounter[4*curState.layerNumber + SKV_VALUE_INVALID ] = 0;
// only calc stats of completed layers
if (layerStats[curState.layerNumber].layerIsCompletedAndInFile) {
for (curState.stateNumber=0; curState.stateNumber<layerStats[curState.layerNumber].knotsInLayer; curState.stateNumber++) {
// get state value
readKnotValueFromDatabase(curState.layerNumber, curState.stateNumber, curStateValue);
statsValueCounter[4*curState.layerNumber + curStateValue]++;
}
// free memory
unloadLayer(curState.layerNumber);
}
// add line
sprintf_s(line, "%d\t%s\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\n",
curState.layerNumber,
getOutputInformation(curState.layerNumber).c_str(),
statsValueCounter[4*curState.layerNumber + SKV_VALUE_GAME_WON ],
statsValueCounter[4*curState.layerNumber + SKV_VALUE_GAME_LOST ],
statsValueCounter[4*curState.layerNumber + SKV_VALUE_GAME_DRAWN],
statsValueCounter[4*curState.layerNumber + SKV_VALUE_INVALID ],
layerStats[curState.layerNumber].knotsInLayer,
layerStats[curState.layerNumber].numSuccLayers,
layerStats[curState.layerNumber].partnerLayer,
layerStats[curState.layerNumber].sizeInBytes,
layerStats[curState.layerNumber].succLayers[0],
layerStats[curState.layerNumber].succLayers[1]);
text += line;
}
// write to file and close it
WriteFile(statFile, text.c_str(), (DWORD) text.length(), &dwBytesWritten, NULL);
CloseHandle(statFile);
SAFE_DELETE_ARRAY(statsValueCounter);
return true;
}
//-----------------------------------------------------------------------------
// Name: anyArrawInfoToUpdate()
// Desc: called by MAIN-thread in pMiniMax->csOsPrint critical-section
//-----------------------------------------------------------------------------
bool miniMax::anyArrawInfoToUpdate()
{
return (arrayInfos.arrayInfosToBeUpdated.size()>0);
}
//-----------------------------------------------------------------------------
// Name: getArrayInfoForUpdate()
// Desc: called by MAIN-thread in pMiniMax->csOsPrint critical-section
//-----------------------------------------------------------------------------
miniMax::arrayInfoChange miniMax::getArrayInfoForUpdate()
{
miniMax::arrayInfoChange tmp = arrayInfos.arrayInfosToBeUpdated.front();
arrayInfos.arrayInfosToBeUpdated.pop_front();
return tmp;
}
//-----------------------------------------------------------------------------
// Name: getCurrentActionStr()
// Desc: called by MAIN-thread in pMiniMax->csOsPrint critical-section
//-----------------------------------------------------------------------------
LPWSTR miniMax::getCurrentActionStr()
{
switch (curCalculationActionId)
{
case MM_ACTION_INIT_RETRO_ANAL : return L"initiating retro-analysis";
case MM_ACTION_PREPARE_COUNT_ARRAY : return L"preparing count arrays";
case MM_ACTION_PERFORM_RETRO_ANAL : return L"performing retro analysis";
case MM_ACTION_PERFORM_ALPHA_BETA : return L"performing alpha-beta-algorithmn";
case MM_ACTION_TESTING_LAYER : return L"testing calculated layer";
case MM_ACTION_SAVING_LAYER_TO_FILE : return L"saving layer to file";
case MM_ACTION_CALC_LAYER_STATS : return L"making layer statistics";
case MM_ACTION_NONE : return L"none";
default: return L"undefined";
}
}
//-----------------------------------------------------------------------------
// Name: getCurrentCalculatedLayer()
// Desc: called by MAIN-thread in pMiniMax->csOsPrint critical-section
//-----------------------------------------------------------------------------
void miniMax::getCurrentCalculatedLayer(vector<unsigned int> &layers)
{
// when retro-analysis is used than two layers are calculated at the same time
if (shallRetroAnalysisBeUsed(curCalculatedLayer) && layerStats[curCalculatedLayer].partnerLayer != curCalculatedLayer) {
layers.resize(2);
layers[0] = curCalculatedLayer;
layers[1] = layerStats[curCalculatedLayer].partnerLayer;
} else {
layers.resize(1);
layers[0] = curCalculatedLayer;
}
}
//-----------------------------------------------------------------------------
// Name: arrayInfoContainer::addArray()
// Desc: Caution: layerNumber and type must be a unique pair!
// called by single CALCULATION-thread
//-----------------------------------------------------------------------------
void miniMax::arrayInfoContainer::addArray(unsigned int layerNumber, unsigned int type, long long size, long long compressedSize)
{
// create new info object and add to list
EnterCriticalSection(&c->csOsPrint);
arrayInfoStruct ais;
ais.belongsToLayer = layerNumber;
ais.compressedSizeInBytes = compressedSize;
ais.sizeInBytes = size;
ais.type = type;
ais.updateCounter = 0;
listArrays.push_back(ais);
// notify cahnge
arrayInfoChange aic;
aic.arrayInfo = &listArrays.back();
aic.itemIndex = (unsigned int) listArrays.size() - 1;
arrayInfosToBeUpdated.push_back(aic);
// save pointer of info in vector for direct access
vectorArrays[layerNumber*arrayInfoStruct::numArrayTypes + type] = (--listArrays.end());
// update GUI
if (c->userPrintFunc != NULL) {
c->userPrintFunc(c->pDataForUserPrintFunc);
}
LeaveCriticalSection(&c->csOsPrint);
}
//-----------------------------------------------------------------------------
// Name: arrayInfoContainer::removeArray()
// Desc: called by single CALCULATION-thread
//-----------------------------------------------------------------------------
void miniMax::arrayInfoContainer::removeArray(unsigned int layerNumber, unsigned int type, long long size, long long compressedSize)
{
// find info object in list
EnterCriticalSection(&c->csOsPrint);
if (vectorArrays.size() > layerNumber*arrayInfoStruct::numArrayTypes + type) {
list<arrayInfoStruct>::iterator itr = vectorArrays[layerNumber*arrayInfoStruct::numArrayTypes + type];
if (itr != listArrays.end()) {
// does sizes fit?
if (itr->belongsToLayer != layerNumber || itr->type!=type || itr->sizeInBytes!=size || itr->compressedSizeInBytes!=compressedSize) {
c->falseOrStop();
}
// notify cahnge
arrayInfoChange aic;
aic.arrayInfo = NULL;
aic.itemIndex = (unsigned int) std::distance(listArrays.begin(), itr);
arrayInfosToBeUpdated.push_back(aic);
// delete tem from list
listArrays.erase(itr);
}
}
// update GUI
if (c->userPrintFunc != NULL) {
c->userPrintFunc(c->pDataForUserPrintFunc);
}
LeaveCriticalSection(&c->csOsPrint);
}
//-----------------------------------------------------------------------------
// Name: arrayInfoContainer::updateArray()
// Desc: called by mltiple CALCULATION-thread
//-----------------------------------------------------------------------------
void miniMax::arrayInfoContainer::updateArray(unsigned int layerNumber, unsigned int type)
{
// find info object in list
list<arrayInfoStruct>::iterator itr = vectorArrays[layerNumber*arrayInfoStruct::numArrayTypes + type];
itr->updateCounter++;
if (itr->updateCounter>arrayInfoStruct::updateCounterThreshold) {
// notify cahnge
EnterCriticalSection(&c->csOsPrint);
arrayInfoChange aic;
aic.arrayInfo = &(*itr);
aic.itemIndex = (unsigned int) std::distance(listArrays.begin(), itr);
arrayInfosToBeUpdated.push_back(aic);
// update GUI
if (c->userPrintFunc != NULL) {
c->userPrintFunc(c->pDataForUserPrintFunc);
}
itr->updateCounter = 0;
LeaveCriticalSection(&c->csOsPrint);
}
}

View File

@ -0,0 +1,550 @@
/*********************************************************************
miniMax_test.cpp
Copyright (c) Thomas Weber. All rights reserved.
Licensed under the MIT License.
https://github.com/madweasel/madweasels-cpp
\*********************************************************************/
#include "miniMax.h"
//-----------------------------------------------------------------------------
// Name: testLayer()
// Desc:
//-----------------------------------------------------------------------------
bool miniMax::testLayer(unsigned int layerNumber)
{
// Locals
unsigned int curThreadNo;
unsigned int returnValue;
// database open?
if (hFileShortKnotValues == NULL || hFilePlyInfo == NULL) {
PRINT(0, this, "ERROR: Database file not open!");
return falseOrStop();
}
// output
PRINT(1, this, endl << "*** Test each state in layer: " << layerNumber << " ***");
PRINT(1, this, (getOutputInformation(layerNumber)));
// prepare parameters for multithreading
skvfHeader.completed = false;
layerInDatabase = false;
numStatesProcessed = 0;
curCalculatedLayer = layerNumber;
curCalculationActionId = MM_ACTION_TESTING_LAYER;
testLayersVars *tlVars = new testLayersVars[threadManager.getNumThreads()];
for (curThreadNo=0; curThreadNo<threadManager.getNumThreads(); curThreadNo++) {
tlVars[curThreadNo].curThreadNo = curThreadNo;
tlVars[curThreadNo].pMiniMax = this;
tlVars[curThreadNo].layerNumber = layerNumber;
tlVars[curThreadNo].statesProcessed = 0;
tlVars[curThreadNo].subValueInDatabase = new twoBit [maxNumBranches];
tlVars[curThreadNo].subPlyInfos = new plyInfoVarType [maxNumBranches];
tlVars[curThreadNo].hasCurPlayerChanged = new bool [maxNumBranches];
}
// process each state in the current layer
returnValue = threadManager.executeParallelLoop(testLayerThreadProc, (void*) tlVars, sizeof(testLayersVars), TM_SCHEDULE_STATIC, 0, layerStats[layerNumber].knotsInLayer - 1,1);
switch (returnValue)
{
case TM_RETURN_VALUE_OK:
case TM_RETURN_VALUE_EXECUTION_CANCELLED:
// reduce and delete thread specific data
for (numStatesProcessed=0, curThreadNo=0; curThreadNo<threadManager.getNumThreads(); curThreadNo++) {
numStatesProcessed += tlVars[curThreadNo].statesProcessed;
SAFE_DELETE_ARRAY(tlVars[curThreadNo].subValueInDatabase);
SAFE_DELETE_ARRAY(tlVars[curThreadNo].hasCurPlayerChanged);
SAFE_DELETE_ARRAY(tlVars[curThreadNo].subPlyInfos);
}
SAFE_DELETE_ARRAY(tlVars);
if (returnValue == TM_RETURN_VALUE_EXECUTION_CANCELLED) {
PRINT(0,this, "Main thread: Execution cancelled by user");
return false; // ... better would be to return a cancel-specific value
} else {
break;
}
default:
case TM_RETURN_VALUE_INVALID_PARAM:
case TM_RETURN_VALUE_UNEXPECTED_ERROR:
return falseOrStop();
}
// layer is not ok
if (numStatesProcessed < layerStats[layerNumber].knotsInLayer) {
PRINT(0, this, "DATABASE ERROR IN LAYER " << layerNumber);
return falseOrStop();
// layer is ok
} else {
PRINT(1, this, " TEST PASSED !" << endl << endl);
return true;
}
}
//-----------------------------------------------------------------------------
// Name: testLayerThreadProc()
// Desc:
//-----------------------------------------------------------------------------
DWORD miniMax::testLayerThreadProc(void* pParameter, int index)
{
// locals
testLayersVars * tlVars = (testLayersVars*) pParameter;
miniMax * m = tlVars->pMiniMax;
unsigned int layerNumber = tlVars->layerNumber;
unsigned int stateNumber = index;
unsigned int threadNo = tlVars->curThreadNo;
twoBit * subValueInDatabase = tlVars->subValueInDatabase;
plyInfoVarType * subPlyInfos = tlVars->subPlyInfos;
bool * hasCurPlayerChanged = tlVars->hasCurPlayerChanged;
twoBit shortValueInDatabase;
plyInfoVarType numPliesTillCurState;
twoBit shortValueInGame;
float floatValueInGame;
plyInfoVarType min, max;
unsigned int numPossibilities;
unsigned int i, j;
unsigned int tmpStateNumber, tmpLayerNumber;
unsigned int * idPossibility;
void * pPossibilities;
void * pBackup;
bool isOpponentLevel;
bool invalidLayerOrStateNumber;
bool layerInDatabaseAndCompleted;
// output
tlVars->statesProcessed++;
if (tlVars->statesProcessed % OUTPUT_EVERY_N_STATES == 0) {
m->numStatesProcessed += OUTPUT_EVERY_N_STATES;
PRINT(0, m, m->numStatesProcessed << " states of " << m->layerStats[layerNumber].knotsInLayer << " tested");
}
// situation already existend in database ?
m->readKnotValueFromDatabase(layerNumber, stateNumber, shortValueInDatabase);
m->readPlyInfoFromDatabase (layerNumber, stateNumber, numPliesTillCurState);
// prepare the situation
if (!m->setSituation(threadNo, layerNumber, stateNumber)) {
// when situation cannot be constructed then state must be marked as invalid in database
if (shortValueInDatabase != SKV_VALUE_INVALID || numPliesTillCurState != PLYINFO_VALUE_INVALID) {
PRINT(0, m, "ERROR: DATABASE ERROR IN LAYER " << layerNumber << " AND STATE " << stateNumber << ": Could not set situation, but value is not invalid.");
goto errorInDatabase;
} else {
return TM_RETURN_VALUE_OK;
}
}
// debug information
if (m->verbosity > 5) {
PRINT(5, m, "layer: " << layerNumber << " state: " << stateNumber);
m->printField(threadNo, shortValueInDatabase);
}
// get number of possiblities
m->setOpponentLevel(threadNo, false);
idPossibility = m->getPossibilities(threadNo, &numPossibilities, &isOpponentLevel, &pPossibilities);
// unable to move
if (numPossibilities == 0) {
// get ingame value
m->getValueOfSituation(threadNo, floatValueInGame, shortValueInGame);
// compare database with game
if (shortValueInDatabase != shortValueInGame || numPliesTillCurState != 0) { PRINT(0,m, "ERROR: DATABASE ERROR IN LAYER " << layerNumber << " AND STATE " << stateNumber << ": Number of possibilities is zero, but knot value is not invalid or ply info equal zero."); goto errorInDatabase; }
if (shortValueInDatabase == SKV_VALUE_INVALID) { PRINT(0,m, "ERROR: DATABASE ERROR IN LAYER " << layerNumber << " AND STATE " << stateNumber << ": Number of possibilities is zero, but knot value is invalid."); goto errorInDatabase; }
} else {
// check each possible move
for (i=0; i<numPossibilities; i++) {
// move
m->move(threadNo, idPossibility[i], isOpponentLevel, &pBackup, pPossibilities);
// get database value
m->readKnotValueFromDatabase(threadNo, tmpLayerNumber, tmpStateNumber, subValueInDatabase[i], invalidLayerOrStateNumber, layerInDatabaseAndCompleted);
m->readPlyInfoFromDatabase (tmpLayerNumber, tmpStateNumber, subPlyInfos[i]);
hasCurPlayerChanged[i] = (m->getOpponentLevel(threadNo) == true);
// debug information
if (m->verbosity > 5) {
PRINT(5, m, "layer: " << tmpLayerNumber << " state: " << tmpStateNumber << " value: " << (int) subValueInDatabase[i]);
m->printField(threadNo, subValueInDatabase[i]);
}
// if layer or state number is invalid then value of testes state must be invalid
if (invalidLayerOrStateNumber && shortValueInDatabase != SKV_VALUE_INVALID) { PRINT(0,m, "ERROR: DATABASE ERROR IN LAYER " << layerNumber << " AND STATE " << stateNumber << ": Succeding state has invalid layer (" << tmpLayerNumber << ")or state number (" << tmpStateNumber << "), but tested state is not marked as invalid."); goto errorInDatabase; }
// BUG: Does not work because, layer 101 is calculated before 105, although removing a stone does need this jump.
// if (!layerInDatabaseAndCompleted) { PRINT(0,m, "ERROR: DATABASE ERROR IN LAYER " << layerNumber << " AND STATE " << stateNumber << ": Succeding state " << tmpStateNumber << " in an uncalculated layer " << tmpLayerNumber << "! Calc layer first!"); goto errorInDatabase; }
// undo move
m->undo(threadNo, idPossibility[i], isOpponentLevel, pBackup, pPossibilities);
}
// value possible?
switch (shortValueInDatabase) {
case SKV_VALUE_GAME_LOST :
// all possible moves must be lost for the current player or won for the opponent
for (i=0; i<numPossibilities; i++) { if (subValueInDatabase[i] != ((hasCurPlayerChanged[i]) ? SKV_VALUE_GAME_WON : SKV_VALUE_GAME_LOST) && subValueInDatabase[i] != SKV_VALUE_INVALID) {
PRINT(0,m, "ERROR: DATABASE ERROR IN LAYER " << layerNumber << " AND STATE " << stateNumber << ": All possible moves must be lost for the current player or won for the opponent");
goto errorInDatabase;
}}
// not all options can be invalid
for (j=0, i=0; i<numPossibilities; i++) { if (subValueInDatabase[i] == SKV_VALUE_INVALID) {
j++;
}}
if (j == numPossibilities) {
PRINT(0,m, "DATABASE ERROR IN LAYER " << layerNumber << " AND STATE " << stateNumber << ". Not all options can be invalid");
}
// ply info must be max(subPlyInfos[]+1)
max = 0;
for (i=0; i<numPossibilities; i++) {
if (subValueInDatabase[i] == ((hasCurPlayerChanged[i]) ? SKV_VALUE_GAME_WON : SKV_VALUE_GAME_LOST)) {
if (subPlyInfos[i] + 1 > max) {
max = subPlyInfos[i] + 1;
}
}
}
if (numPliesTillCurState>PLYINFO_VALUE_DRAWN) {
PRINT(0,m, "DATABASE ERROR IN LAYER " << layerNumber << " AND STATE " << stateNumber << ": Knot value is LOST, but numPliesTillCurState is bigger than PLYINFO_MAX_VALUE.");
goto errorInDatabase;
}
if (numPliesTillCurState!=max) {
PRINT(0,m, "DATABASE ERROR IN LAYER " << layerNumber << " AND STATE " << stateNumber << ": Number of needed plies is not maximal for LOST state.");
goto errorInDatabase;
}
break;
case SKV_VALUE_GAME_WON :
// at least one possible move must be lost for the opponent or won for the current player
for (i=0; i<numPossibilities; i++) {
// if (subValueInDatabase[i] == SKV_VALUE_INVALID) { PRINT(0,m, "DATABASE ERROR IN LAYER " << layerNumber << " AND STATE " << stateNumber << ": At least one possible move must be lost for the opponent or won for the current player. But subValueInDatabase[i] == SKV_VALUE_INVALID."); goto errorInDatabase; }
if (subValueInDatabase[i] == ((hasCurPlayerChanged[i]) ? SKV_VALUE_GAME_LOST : SKV_VALUE_GAME_WON)) i = numPossibilities;
}
if (i==numPossibilities) {
PRINT(0,m, "DATABASE ERROR IN LAYER " << layerNumber << " AND STATE " << stateNumber << ": At least one possible move must be lost for the opponent or won for the current player.");
goto errorInDatabase;
}
// ply info must be min(subPlyInfos[]+1)
min = PLYINFO_VALUE_DRAWN;
for (i=0; i<numPossibilities; i++) {
if (subValueInDatabase[i] == ((hasCurPlayerChanged[i]) ? SKV_VALUE_GAME_LOST : SKV_VALUE_GAME_WON)) {
if (subPlyInfos[i] + 1 < min) {
min = subPlyInfos[i] + 1;
}
}
}
if (numPliesTillCurState>PLYINFO_VALUE_DRAWN) {
PRINT(0,m, "DATABASE ERROR IN LAYER " << layerNumber << " AND STATE " << stateNumber << ": Knot value is WON, but numPliesTillCurState is bigger than PLYINFO_MAX_VALUE.");
goto errorInDatabase;
}
if (numPliesTillCurState!=min) {
PRINT(0,m, "DATABASE ERROR IN LAYER " << layerNumber << " AND STATE " << stateNumber << ": Number of needed plies is not minimal for WON state.");
goto errorInDatabase;
}
break;
case SKV_VALUE_GAME_DRAWN:
// all possible moves must be won for the opponent, lost for the current player or drawn
for (j=0,i=0; i<numPossibilities; i++) {
// if (subValueInDatabase[i] == SKV_VALUE_INVALID) { PRINT(0,m, "DATABASE ERROR IN LAYER " << layerNumber << " AND STATE " << stateNumber << ": All possible moves must be won for the opponent, lost for the current player or drawn. But subValueInDatabase[i] == SKV_VALUE_INVALID."); goto errorInDatabase; }
if (subValueInDatabase[i] != ((hasCurPlayerChanged[i]) ? SKV_VALUE_GAME_WON : SKV_VALUE_GAME_LOST)
&& subValueInDatabase[i] != SKV_VALUE_GAME_DRAWN
&& subValueInDatabase[i] != SKV_VALUE_INVALID) { PRINT(0,m, "DATABASE ERROR IN LAYER " << layerNumber << " AND STATE " << stateNumber << ": All possible moves must be won for the opponent, lost for the current player or drawn."); goto errorInDatabase; }
if (subValueInDatabase[i] == SKV_VALUE_GAME_DRAWN) j = 1;
}
// at least one succeding state must be drawn
if (j == 0) { PRINT(0,m, "DATABASE ERROR IN LAYER " << layerNumber << " AND STATE " << stateNumber << ": At least one succeding state must be drawn."); goto errorInDatabase; }
// ply info must also be drawn
if (numPliesTillCurState != PLYINFO_VALUE_DRAWN) { PRINT(0,m, "DATABASE ERROR IN LAYER " << layerNumber << " AND STATE " << stateNumber << ": Knot value is drawn but ply info is not!"); goto errorInDatabase; }
break;
case SKV_VALUE_INVALID:
// if setSituation() returned true but state value is invalid, then all following states must be invalid
for (i=0; i<numPossibilities; i++) {
if (subValueInDatabase[i] != SKV_VALUE_INVALID) break;
}
if (i!=numPossibilities) {
PRINT(0,m, "DATABASE ERROR IN LAYER " << layerNumber << " AND STATE " << stateNumber << ": If setSituation() returned true but state value is invalid, then all following states must be invalid."); goto errorInDatabase;
}
// ply info must also be invalid
if (numPliesTillCurState != PLYINFO_VALUE_INVALID) { PRINT(0,m, "DATABASE ERROR IN LAYER " << layerNumber << " AND STATE " << stateNumber << ": Knot value is invalid but ply info is not!"); goto errorInDatabase; }
break;
}
}
return TM_RETURN_VALUE_OK;
errorInDatabase:
// terminate all threads
return TM_RETURN_VALUE_TERMINATE_ALL_THREADS;
}
//-----------------------------------------------------------------------------
// Name: testState()
// Desc:
//-----------------------------------------------------------------------------
bool miniMax::testState(unsigned int layerNumber, unsigned int stateNumber)
{
// locals
testLayersVars tlVars;
bool result;
// prepare parameters for multithreading
tlVars.curThreadNo = 0;
tlVars.pMiniMax = this;
tlVars.layerNumber = layerNumber;
tlVars.statesProcessed = 0;
tlVars.subValueInDatabase = new twoBit [maxNumBranches];
tlVars.subPlyInfos = new plyInfoVarType [maxNumBranches];
tlVars.hasCurPlayerChanged = new bool [maxNumBranches];
if (testLayerThreadProc(&tlVars, stateNumber) != TM_RETURN_VALUE_OK) result = false;
delete [] tlVars.subValueInDatabase;
delete [] tlVars.subPlyInfos;
delete [] tlVars.hasCurPlayerChanged;
return result;
}
//-----------------------------------------------------------------------------
// Name: testSetSituationAndGetPoss()
// Desc:
//-----------------------------------------------------------------------------
bool miniMax::testSetSituationAndGetPoss(unsigned int layerNumber)
{
// Locals
unsigned int curThreadNo;
unsigned int returnValue;
// output
PRINT(1, this, endl << "*** Test each state in layer: " << layerNumber << " ***");
PRINT(1, this, (getOutputInformation(layerNumber)));
// prepare parameters for multithreading
numStatesProcessed = 0;
curCalculationActionId = MM_ACTION_TESTING_LAYER;
testLayersVars *tlVars = new testLayersVars[threadManager.getNumThreads()];
for (curThreadNo=0; curThreadNo<threadManager.getNumThreads(); curThreadNo++) {
tlVars[curThreadNo].curThreadNo = curThreadNo;
tlVars[curThreadNo].pMiniMax = this;
tlVars[curThreadNo].layerNumber = layerNumber;
tlVars[curThreadNo].statesProcessed = 0;
tlVars[curThreadNo].subValueInDatabase = new twoBit [maxNumBranches];
tlVars[curThreadNo].subPlyInfos = new plyInfoVarType [maxNumBranches];
tlVars[curThreadNo].hasCurPlayerChanged = new bool [maxNumBranches];
}
// process each state in the current layer
returnValue = threadManager.executeParallelLoop(testSetSituationThreadProc, (void*) tlVars, sizeof(testLayersVars), TM_SCHEDULE_STATIC, 0, layerStats[layerNumber].knotsInLayer - 1,1);
switch (returnValue)
{
case TM_RETURN_VALUE_OK:
case TM_RETURN_VALUE_EXECUTION_CANCELLED:
// reduce and delete thread specific data
for (numStatesProcessed=0, curThreadNo=0; curThreadNo<threadManager.getNumThreads(); curThreadNo++) {
numStatesProcessed += tlVars[curThreadNo].statesProcessed;
SAFE_DELETE_ARRAY(tlVars[curThreadNo].subValueInDatabase);
SAFE_DELETE_ARRAY(tlVars[curThreadNo].hasCurPlayerChanged);
SAFE_DELETE_ARRAY(tlVars[curThreadNo].subPlyInfos);
}
SAFE_DELETE_ARRAY(tlVars);
if (returnValue == TM_RETURN_VALUE_EXECUTION_CANCELLED) {
PRINT(0,this, "Main thread: Execution cancelled by user");
return false; // ... better would be to return a cancel-specific value
} else {
break;
}
default:
case TM_RETURN_VALUE_INVALID_PARAM:
case TM_RETURN_VALUE_UNEXPECTED_ERROR:
return falseOrStop();
}
// layer is not ok
if (numStatesProcessed < layerStats[layerNumber].knotsInLayer) {
PRINT(0, this, "DATABASE ERROR IN LAYER " << layerNumber);
return falseOrStop();
// layer is ok
} else {
PRINT(1, this, " TEST PASSED !" << endl << endl);
return true;
}
}
//-----------------------------------------------------------------------------
// Name: testSetSituationThreadProc()
// Desc:
//-----------------------------------------------------------------------------
DWORD miniMax::testSetSituationThreadProc(void* pParameter, int index)
{
// locals
testLayersVars * tlVars = (testLayersVars*) pParameter;
miniMax * m = tlVars->pMiniMax;
unsigned int * idPossibility;
void * pPossibilities;
void * pBackup;
unsigned int curPoss;
float floatValue;
stateAdressStruct curState;
stateAdressStruct subState;
knotStruct knot;
twoBit shortKnotValue = SKV_VALUE_GAME_DRAWN;
curState.layerNumber = tlVars->layerNumber;
curState.stateNumber = index;
// output
tlVars->statesProcessed++;
if (tlVars->statesProcessed % OUTPUT_EVERY_N_STATES == 0) {
m->numStatesProcessed += OUTPUT_EVERY_N_STATES;
PRINT(0, m, m->numStatesProcessed << " states of " << m->layerStats[curState.layerNumber].knotsInLayer << " tested");
}
// set state
if (m->setSituation(tlVars->curThreadNo, curState.layerNumber, curState.stateNumber)) {
m->getValueOfSituation(tlVars->curThreadNo, floatValue, shortKnotValue);
} else {
shortKnotValue = SKV_VALUE_INVALID;
}
// get number of possiblities
idPossibility = m->getPossibilities(tlVars->curThreadNo, &knot.numPossibilities, &knot.isOpponentLevel, &pPossibilities);
// unable to move
if (knot.numPossibilities == 0) {
if (shortKnotValue == SKV_VALUE_GAME_DRAWN) {
PRINT(0, m, "ERROR: Layer " << curState.layerNumber << " and state " << curState.stateNumber << ". setSituation() returned true, although getPossibilities() yields no possible moves.");
return m->falseOrStop();
}
// moving is possible
} else {
if (shortKnotValue == SKV_VALUE_INVALID) {
PRINT(0, m, "ERROR: Moved from layer " << curState.layerNumber << " and state " << curState.stateNumber << " setSituation() returned false, although getPossibilities() yields some possible moves.");
return m->falseOrStop();
}
// check each possibility
for (curPoss=0; curPoss<knot.numPossibilities; curPoss++) {
// move
m->move(tlVars->curThreadNo, idPossibility[curPoss], knot.isOpponentLevel, &pBackup, pPossibilities);
// get state number of succeding state
unsigned int i;
m->getLayerAndStateNumber(tlVars->curThreadNo, i, subState.stateNumber);
subState.layerNumber = i;
// undo move
m->undo(tlVars->curThreadNo, idPossibility[curPoss], knot.isOpponentLevel, pBackup, pPossibilities);
// state reached by move() must not be invalid
if (!m->setSituation(tlVars->curThreadNo, subState.layerNumber, subState.stateNumber)) {
PRINT(0, m, "ERROR: Moved from layer " << curState.layerNumber << " and state " << curState.stateNumber << " to invalid situation layer " << curState.layerNumber << " and state " << curState.stateNumber);
return m->falseOrStop();
}
// set back to current state
m->setSituation(tlVars->curThreadNo, curState.layerNumber, curState.stateNumber);
}
}
return TM_RETURN_VALUE_OK;
//errorInDatabase:
// terminate all threads
return TM_RETURN_VALUE_TERMINATE_ALL_THREADS;
}
//-----------------------------------------------------------------------------
// Name: testIfSymStatesHaveSameValue()
// Desc:
//-----------------------------------------------------------------------------
bool miniMax::testIfSymStatesHaveSameValue(unsigned int layerNumber)
{
// Locals
unsigned int threadNo = 0;
twoBit shortValueInDatabase;
twoBit shortValueOfSymState;
plyInfoVarType numPliesTillCurState;
plyInfoVarType numPliesTillSymState;
unsigned int stateNumber = 0;
unsigned int * symStateNumbers = NULL;
unsigned int numSymmetricStates;
unsigned int i;
// database open?
if (hFileShortKnotValues == NULL || hFilePlyInfo == NULL) {
PRINT(0, this, "ERROR: Database files not open!");
layerNumber = 0;
goto errorInDatabase;
}
// layer completed ?
if (!layerStats[layerNumber].layerIsCompletedAndInFile) {
PRINT(0, this, "ERROR: Layer not in file!");
layerNumber = 0;
goto errorInDatabase;
}
// test if each state has symmetric states with the same value
PRINT(1, this, endl << "testIfSymmetricStatesHaveSameValue - TEST EACH STATE IN LAYER: " << layerNumber);
PRINT(1, this, (getOutputInformation(layerNumber)));
skvfHeader.completed = false;
for (layerInDatabase=false, stateNumber=0; stateNumber<layerStats[layerNumber].knotsInLayer; stateNumber++) {
// output
if (stateNumber % OUTPUT_EVERY_N_STATES == 0) PRINT(1, this, stateNumber << " states of " << layerStats[layerNumber].knotsInLayer << " tested");
// situation already existend in database ?
readKnotValueFromDatabase(layerNumber, stateNumber, shortValueInDatabase);
readPlyInfoFromDatabase(layerNumber, stateNumber, numPliesTillCurState);
// prepare the situation
if (!setSituation(threadNo, layerNumber, stateNumber)) {
// when situation cannot be constructed then state must be marked as invalid in database
if (shortValueInDatabase != SKV_VALUE_INVALID || numPliesTillCurState != PLYINFO_VALUE_INVALID) goto errorInDatabase;
else continue;
}
// get numbers of symmetric states
getSymStateNumWithDoubles(threadNo, &numSymmetricStates, &symStateNumbers);
// save value for all symmetric states
for (i=0; i<numSymmetricStates; i++) {
readKnotValueFromDatabase(layerNumber, symStateNumbers[i], shortValueOfSymState);
readPlyInfoFromDatabase(layerNumber, symStateNumbers[i], numPliesTillSymState);
if (shortValueOfSymState != shortValueInDatabase || numPliesTillCurState != numPliesTillSymState) {
PRINT(2, this, "current tested state " << stateNumber << " has value " << (int) shortValueInDatabase);
setSituation(threadNo, layerNumber, stateNumber);
printField(threadNo, shortValueInDatabase);
PRINT(1, this, "");
PRINT(1, this, "symmetric state " << symStateNumbers[i] << " has value " << (int) shortValueOfSymState);
setSituation(threadNo, layerNumber, symStateNumbers[i]);
printField(threadNo, shortValueOfSymState);
setSituation(threadNo, layerNumber, stateNumber);
}
}
}
// layer is ok
PRINT(0, this, "TEST PASSED !");
return true;
errorInDatabase:
// layer is not ok
if (layerNumber) PRINT(0, this, "DATABASE ERROR IN LAYER " << layerNumber << " AND STATE " << stateNumber);
return falseOrStop();
}

714
src/perfect/muehle.cpp Normal file
View File

@ -0,0 +1,714 @@
/*********************************************************************
muehle.cpp
Copyright (c) Thomas Weber. All rights reserved.
Licensed under the MIT License.
https://github.com/madweasel/madweasels-cpp
\*********************************************************************/
#include "muehle.h"
//-----------------------------------------------------------------------------
// Name: muehle()
// Desc: muehle class constructor
//-----------------------------------------------------------------------------
muehle::muehle()
{
srand( (unsigned)time( NULL ) );
moveLogFrom = NULL;
moveLogTo = NULL;
playerOneKI = NULL;
playerTwoKI = NULL;
movesDone = 0;
field.createField();
initialField.createField();
}
//-----------------------------------------------------------------------------
// Name: ~muehle()
// Desc: muehle class destructor
//-----------------------------------------------------------------------------
muehle::~muehle()
{
deleteArrays();
}
//-----------------------------------------------------------------------------
// Name: deleteArrays()
// Desc: Deletes all arrays the muehle class has created.
//-----------------------------------------------------------------------------
void muehle::deleteArrays()
{
SAFE_DELETE_ARRAY (moveLogFrom);
SAFE_DELETE_ARRAY (moveLogTo );
field.deleteField();
initialField.deleteField();
}
//-----------------------------------------------------------------------------
// Name: beginNewGame()
// Desc: Reinitializes the muehle object.
//-----------------------------------------------------------------------------
void muehle::beginNewGame(muehleKI *firstPlayerKI, muehleKI *secondPlayerKI, int currentPlayer)
{
// free mem
deleteArrays();
// create arrays
field.createField();
initialField.createField();
// calc beginning player
if (currentPlayer == field.playerOne || currentPlayer == field.playerTwo) {
beginningPlayer = currentPlayer;
} else {
beginningPlayer = (rand() % 2) ? field.playerOne : field.playerTwo;
}
field.curPlayer->id = beginningPlayer;
field.oppPlayer->id = (field.curPlayer->id == field.playerTwo) ? field.playerOne : field.playerTwo;
winner = 0;
movesDone = 0;
playerOneKI = firstPlayerKI;
playerTwoKI = secondPlayerKI;
moveLogFrom = new unsigned int[MAX_NUM_MOVES];
moveLogTo = new unsigned int[MAX_NUM_MOVES];
// remember initialField
field.copyField(&initialField);
}
//-----------------------------------------------------------------------------
// Name: startSettingPhase()
// Desc:
//-----------------------------------------------------------------------------
bool muehle::startSettingPhase(muehleKI *firstPlayerKI, muehleKI *secondPlayerKI, int currentPlayer, bool settingPhase)
{
beginNewGame(firstPlayerKI, secondPlayerKI, currentPlayer);
field.settingPhase = settingPhase;
return true;
}
//-----------------------------------------------------------------------------
// Name: setUpCalcPossibleMoves()
// Desc: Calculates and set the number of possible moves for the passed player considering the game state stored in the 'field' variable.
//-----------------------------------------------------------------------------
void muehle::setUpCalcPossibleMoves(playerStruct *player)
{
// locals
unsigned int i, j , k, movingDirection;
for (player->numPossibleMoves=0, i=0; i<fieldStruct::size; i++) { for (j=0; j<fieldStruct::size; j++) {
// is stone from player ?
if (field.field[i] != player->id) continue;
// is destination free ?
if (field.field[j] != field.squareIsFree) continue;
// when current player has only 3 stones he is allowed to spring his stone
if (player->numStones > 3 || field.settingPhase) {
// determine moving direction
for (k=0, movingDirection=4; k<4; k++) if (field.connectedSquare[i][k] == j) movingDirection = k;
// are both squares connected ?
if (movingDirection == 4) continue;
}
// everything is ok
player->numPossibleMoves++;
}}
}
//-----------------------------------------------------------------------------
// Name: setUpSetWarningAndMill()
// Desc:
//-----------------------------------------------------------------------------
void muehle::setUpSetWarningAndMill(unsigned int stone, unsigned int firstNeighbour, unsigned int secondNeighbour)
{
// locals
int rowOwner = field.field[stone];
// mill closed ?
if (rowOwner != field.squareIsFree && field.field[firstNeighbour] == rowOwner && field.field[secondNeighbour] == rowOwner) {
field.stonePartOfMill[stone]++;
field.stonePartOfMill[firstNeighbour]++;
field.stonePartOfMill[secondNeighbour]++;
}
}
//-----------------------------------------------------------------------------
// Name: putStone()
// Desc: Put a stone onto the field during the setting phase.
//-----------------------------------------------------------------------------
bool muehle::putStone(unsigned int pos, int player)
{
// locals
unsigned int i;
unsigned int numberOfMillsCurrentPlayer = 0, numberOfMillsOpponentPlayer = 0;
playerStruct *myPlayer = (player == field.curPlayer->id) ? field.curPlayer : field.oppPlayer;
// check parameters
if (player != fieldStruct::playerOne && player != fieldStruct::playerTwo) return false;
if (pos >= fieldStruct::size) return false;
if (field.field[pos] != field.squareIsFree) return false;
// set stone
field.field[pos] = player;
myPlayer->numStones++;
field.stonesSet++;
// setting phase finished ?
if (field.stonesSet == 18) field.settingPhase = false;
// calc possible moves
setUpCalcPossibleMoves(field.curPlayer);
setUpCalcPossibleMoves(field.oppPlayer);
// zero
for (i=0; i<fieldStruct::size; i++) field.stonePartOfMill[i] = 0;
// go in every direction
for (i=0; i<fieldStruct::size; i++) {
setUpSetWarningAndMill(i, field.neighbour[i][0][0], field.neighbour[i][0][1]);
setUpSetWarningAndMill(i, field.neighbour[i][1][0], field.neighbour[i][1][1]);
}
// since every mill was detected 3 times
for (i=0; i<fieldStruct::size; i++) field.stonePartOfMill[i] /= 3;
// count completed mills
for (i=0; i<fieldStruct::size; i++) {
if (field.field[i] == field.curPlayer->id) numberOfMillsCurrentPlayer += field.stonePartOfMill[i];
else numberOfMillsOpponentPlayer += field.stonePartOfMill[i];
}
numberOfMillsCurrentPlayer /= 3;
numberOfMillsOpponentPlayer /= 3;
// stonesSet & numStonesMissing
if (field.settingPhase) {
// ... This calculation is not correct! It is possible that some mills did not cause a stone removal.
field.curPlayer->numStonesMissing = numberOfMillsOpponentPlayer;
field.oppPlayer->numStonesMissing = numberOfMillsCurrentPlayer - field.stoneMustBeRemoved;
field.stonesSet = field.curPlayer->numStones + field.oppPlayer->numStones + field.curPlayer->numStonesMissing + field.oppPlayer->numStonesMissing;
} else {
field.stonesSet = 18;
field.curPlayer->numStonesMissing = 9 - field.curPlayer->numStones;
field.oppPlayer->numStonesMissing = 9 - field.oppPlayer->numStones;
}
// when opponent is unable to move than current player has won
if ((!field.curPlayer->numPossibleMoves) && (!field.settingPhase)
&& (!field.stoneMustBeRemoved) && (field.curPlayer->numStones > 3)) winner = field.oppPlayer->id;
else if ((field.curPlayer->numStones < 3) && (!field.settingPhase)) winner = field.oppPlayer->id;
else if ((field.oppPlayer->numStones < 3) && (!field.settingPhase)) winner = field.curPlayer->id;
else winner = 0;
// everything is ok
return true;
}
//-----------------------------------------------------------------------------
// Name: settingPhaseHasFinished()
// Desc: This function has to be called when the setting phase has finished.
//-----------------------------------------------------------------------------
bool muehle::settingPhaseHasFinished()
{
// remember initialField
field.copyField(&initialField);
return true;
}
//-----------------------------------------------------------------------------
// Name: getField()
// Desc: Copy the current field state into the array 'pField'.
//-----------------------------------------------------------------------------
bool muehle::getField(int *pField)
{
unsigned int index;
// if no log is available than no game is in progress and field is invalid
if (moveLogFrom == NULL) return false;
for (index=0; index<field.size; index++) {
if (field.warnings[index] != field.noWarning) pField[index] = (int) field.warnings[index];
else pField[index] = field.field[index];
}
return true;
}
//-----------------------------------------------------------------------------
// Name: getLog()
// Desc: Copy the whole history of moves into the passed arrays, which must be of size [MAX_NUM_MOVES].
//-----------------------------------------------------------------------------
void muehle::getLog(unsigned int &numMovesDone, unsigned int *from, unsigned int *to)
{
unsigned int index;
numMovesDone = movesDone;
for (index=0; index<movesDone; index++) {
from[index] = moveLogFrom[index];
to [index] = moveLogTo [index];
}
}
//-----------------------------------------------------------------------------
// Name: setNextPlayer()
// Desc: Current player and opponent player are switched in the field struct.
//-----------------------------------------------------------------------------
void muehle::setNextPlayer()
{
playerStruct *tmpPlayer;
tmpPlayer = field.curPlayer;
field.curPlayer = field.oppPlayer;
field.oppPlayer = tmpPlayer;
}
//-----------------------------------------------------------------------------
// Name: isCurrentPlayerHuman()
// Desc: Returns true if the current player is not assigned to an AI.
//-----------------------------------------------------------------------------
bool muehle::isCurrentPlayerHuman()
{
if (field.curPlayer->id == field.playerOne) return (playerOneKI == NULL) ? true : false;
else return (playerTwoKI == NULL) ? true : false;
}
//-----------------------------------------------------------------------------
// Name: isOpponentPlayerHuman()
// Desc: Returns true if the opponent player is not assigned to an AI.
//-----------------------------------------------------------------------------
bool muehle::isOpponentPlayerHuman()
{
if (field.oppPlayer->id == field.playerOne) return (playerOneKI == NULL) ? true : false;
else return (playerTwoKI == NULL) ? true : false;
}
//-----------------------------------------------------------------------------
// Name: setKI()
// Desc: Assigns an AI to a player.
//-----------------------------------------------------------------------------
void muehle::setKI(int player, muehleKI *KI)
{
if (player == field.playerOne) { playerOneKI = KI; }
if (player == field.playerTwo) { playerTwoKI = KI; }
}
//-----------------------------------------------------------------------------
// Name: getChoiceOfSpecialKI()
// Desc: Returns the move the passed AI would do.
//-----------------------------------------------------------------------------
void muehle::getChoiceOfSpecialKI(muehleKI *KI, unsigned int *pushFrom, unsigned int *pushTo)
{
fieldStruct theField;
*pushFrom = field.size;
*pushTo = field.size;
theField.createField();
field.copyField(&theField);
if (KI != NULL && (field.settingPhase || field.curPlayer->numPossibleMoves > 0) && winner == 0) KI->play(&theField, pushFrom, pushTo);
theField.deleteField();
}
//-----------------------------------------------------------------------------
// Name: getComputersChoice()
// Desc: Returns the move the AI of the current player would do.
//-----------------------------------------------------------------------------
void muehle::getComputersChoice(unsigned int *pushFrom, unsigned int *pushTo)
{
fieldStruct theField;
*pushFrom = field.size;
*pushTo = field.size;
theField.createField();
field.copyField(&theField);
if ((field.settingPhase || field.curPlayer->numPossibleMoves > 0) && winner == 0) {
if (field.curPlayer->id == field.playerOne) { if (playerOneKI != NULL) playerOneKI->play(&theField, pushFrom, pushTo); }
else { if (playerTwoKI != NULL) playerTwoKI->play(&theField, pushFrom, pushTo); }
}
theField.deleteField();
}
//-----------------------------------------------------------------------------
// Name: isNormalMovePossible()
// Desc: 'Normal' in this context means, by moving the stone along a connection without jumping.
//-----------------------------------------------------------------------------
bool muehle::isNormalMovePossible(unsigned int from, unsigned int to, playerStruct *player)
{
// locals
unsigned int movingDirection, i;
// parameter ok ?
if (from >= field.size) return false;
if (to >= field.size) return false;
// is stone from player ?
if (field.field[from] != player->id) return false;
// is destination free ?
if (field.field[to] != field.squareIsFree) return false;
// when current player has only 3 stones he is allowed to spring his stone
if (player->numStones > 3 || field.settingPhase) {
// determine moving direction
for (i=0, movingDirection=4; i<4; i++) if (field.connectedSquare[from][i] == to) movingDirection = i;
// are both squares connected ?
if (movingDirection == 4) return false;
}
// everything is ok
return true;
}
//-----------------------------------------------------------------------------
// Name: calcPossibleMoves()
// Desc: ...
//-----------------------------------------------------------------------------
void muehle::calcPossibleMoves(playerStruct *player)
{
// locals
unsigned int i, j;
// zero
for (i=0; i<MAX_NUM_POS_MOVES; i++) player->posTo[i] = field.size;
for (i=0; i<MAX_NUM_POS_MOVES; i++) player->posFrom[i] = field.size;
// calc
for (player->numPossibleMoves=0, i=0; i<field.size; i++) { for (j=0; j<field.size; j++) { if (isNormalMovePossible(i, j, player)) {
player->posFrom[player->numPossibleMoves] = i;
player->posTo [player->numPossibleMoves] = j;
player->numPossibleMoves++;
}}}
// stoneMoveAble
for (i=0; i<field.size; i++) { for (j=0; j<4; j++) {
if (field.field[i] == player->id) field.stoneMoveAble[i][j] = isNormalMovePossible(i, field.connectedSquare[i][j], player);
else field.stoneMoveAble[i][j] = false;
}}
}
//-----------------------------------------------------------------------------
// Name: setWarningAndMill()
// Desc:
//-----------------------------------------------------------------------------
void muehle::setWarningAndMill(unsigned int stone, unsigned int firstNeighbour, unsigned int secondNeighbour, bool isNewStone)
{
// locals
int rowOwner = field.field[stone];
unsigned int rowOwnerWarning = (rowOwner == field.playerOne) ? field.playerOneWarning : field.playerTwoWarning;
// mill closed ?
if (rowOwner != field.squareIsFree && field.field[firstNeighbour] == rowOwner && field.field[secondNeighbour] == rowOwner) {
field.stonePartOfMill[stone]++;
field.stonePartOfMill[firstNeighbour]++;
field.stonePartOfMill[secondNeighbour]++;
if (isNewStone) field.stoneMustBeRemoved = 1;
}
//warning ?
if (rowOwner != field.squareIsFree && field.field[firstNeighbour ] == field.squareIsFree && field.field[secondNeighbour] == rowOwner) field.warnings[firstNeighbour ] |= rowOwnerWarning;
if (rowOwner != field.squareIsFree && field.field[secondNeighbour] == field.squareIsFree && field.field[firstNeighbour ] == rowOwner) field.warnings[secondNeighbour] |= rowOwnerWarning;
}
//-----------------------------------------------------------------------------
// Name: updateMillsAndWarnings()
// Desc:
//-----------------------------------------------------------------------------
void muehle::updateMillsAndWarnings(unsigned int newStone)
{
// locals
unsigned int i;
bool atLeastOneStoneRemoveAble;
// zero
for (i=0; i<field.size; i++) field.stonePartOfMill[i] = 0;
for (i=0; i<field.size; i++) field.warnings[i] = field.noWarning;
field.stoneMustBeRemoved = 0;
// go in every direction
for (i=0; i<field.size; i++) {
setWarningAndMill(i, field.neighbour[i][0][0], field.neighbour[i][0][1], i == newStone);
setWarningAndMill(i, field.neighbour[i][1][0], field.neighbour[i][1][1], i == newStone);
}
// since every mill was detected 3 times
for (i=0; i<field.size; i++) field.stonePartOfMill[i] /= 3;
// no stone must be removed if each belongs to a mill
for (atLeastOneStoneRemoveAble = false, i=0; i<field.size; i++) if (field.stonePartOfMill[i] == 0 && field.field[i] == field.oppPlayer->id) atLeastOneStoneRemoveAble = true;
if (!atLeastOneStoneRemoveAble) field.stoneMustBeRemoved = 0;
}
//-----------------------------------------------------------------------------
// Name: moveStone()
// Desc:
//-----------------------------------------------------------------------------
bool muehle::moveStone(unsigned int pushFrom, unsigned int pushTo)
{
// avoid index override
if (movesDone >= MAX_NUM_MOVES)
return false;
// is game still running ?
if (winner)
return false;
// handle the remove of a stone
if (field.stoneMustBeRemoved) {
// parameter ok ?
if (pushFrom >= field.size)
return false;
// is it stone from the opponent ?
if (field.field[pushFrom] != field.oppPlayer->id)
return false;
// is stone not part of mill?
if (field.stonePartOfMill[pushFrom])
return false;
// remove stone
moveLogFrom[movesDone] = pushFrom;
moveLogTo [movesDone] = field.size;
field.field[pushFrom] = field.squareIsFree;
field.oppPlayer->numStonesMissing++;
field.oppPlayer->numStones--;
field.stoneMustBeRemoved--;
movesDone++;
// is the game finished ?
if ((field.oppPlayer->numStones < 3) && (!field.settingPhase)) winner = field.curPlayer->id;
// update warnings & mills
updateMillsAndWarnings(field.size);
// calc possibilities
calcPossibleMoves(field.curPlayer);
calcPossibleMoves(field.oppPlayer);
// is opponent unable to move ?
if (field.oppPlayer->numPossibleMoves == 0 && !field.settingPhase) winner = field.curPlayer->id;
// next player
if (!field.stoneMustBeRemoved) setNextPlayer();
// everything is ok
return true;
// handle setting phase
} else if (field.settingPhase) {
// parameter ok ?
if (pushTo >= field.size)
return false;
// is destination free ?
if (field.field[pushTo] != field.squareIsFree)
return false;
// set stone
moveLogFrom[movesDone] = field.size;
moveLogTo [movesDone] = pushTo;
field.field[pushTo] = field.curPlayer->id;
field.curPlayer->numStones++;
field.stonesSet++;
movesDone++;
// update warnings & mills
updateMillsAndWarnings(pushTo);
// calc possibilities
calcPossibleMoves(field.curPlayer);
calcPossibleMoves(field.oppPlayer);
// setting phase finished ?
if (field.stonesSet == 18) field.settingPhase = false;
// is opponent unable to move ?
if (field.oppPlayer->numPossibleMoves == 0 && !field.settingPhase) winner = field.curPlayer->id;
// next player
if (!field.stoneMustBeRemoved) setNextPlayer();
// everything is ok
return true;
// normal move
} else {
// is move possible ?
if (!isNormalMovePossible(pushFrom, pushTo, field.curPlayer))
return false;
// move stone
moveLogFrom[movesDone] = pushFrom;
moveLogTo [movesDone] = pushTo;
field.field[pushFrom] = field.squareIsFree;
field.field[pushTo] = field.curPlayer->id;
movesDone++;
// update warnings & mills
updateMillsAndWarnings(pushTo);
// calc possibilities
calcPossibleMoves(field.curPlayer);
calcPossibleMoves(field.oppPlayer);
// is opponent unable to move ?
if (field.oppPlayer->numPossibleMoves == 0 && !field.settingPhase) winner = field.curPlayer->id;
// next player
if (!field.stoneMustBeRemoved) setNextPlayer();
// everything is ok
return true;
}
}
//-----------------------------------------------------------------------------
// Name: setCurrentGameState()
// Desc: Set an arbitrary game state as the current one.
//-----------------------------------------------------------------------------
bool muehle::setCurrentGameState(fieldStruct *curState)
{
curState->copyField(&field);
winner = 0;
movesDone = 0;
if ((field.curPlayer->numStones < 3) && (!field.settingPhase)) winner = field.oppPlayer->id;
if ((field.oppPlayer->numStones < 3) && (!field.settingPhase)) winner = field.curPlayer->id;
if ((field.curPlayer->numPossibleMoves == 0) && (!field.settingPhase)) winner = field.oppPlayer->id;
return true;
}
//-----------------------------------------------------------------------------
// Name: compareWithField()
// Desc: Compares the current 'field' variable with the passed one. 'stoneMoveAble[]' is ignored.
//-----------------------------------------------------------------------------
bool muehle::compareWithField(fieldStruct *compareField)
{
unsigned int i, j;
bool ret = true;
if (!comparePlayers(field.curPlayer, compareField->curPlayer)) { cout << "error - curPlayer differs!" << endl; ret = false; }
if (!comparePlayers(field.oppPlayer, compareField->oppPlayer)) { cout << "error - oppPlayer differs!" << endl; ret = false; }
if (field.stonesSet != compareField->stonesSet) { cout << "error - stonesSet differs!" << endl; ret = false; }
if (field.settingPhase != compareField->settingPhase) { cout << "error - settingPhase differs!" << endl; ret = false; }
if (field.stoneMustBeRemoved != compareField->stoneMustBeRemoved) { cout << "error - stoneMustBeRemoved differs!" << endl; ret = false; }
for (i=0; i<field.size; i++) {
if (field.field[i] != compareField->field[i]) { cout << "error - field[] differs!" << endl; ret = false; }
if (field.warnings[i] != compareField->warnings[i]) { cout << "error - warnings[] differs!" << endl; ret = false; }
if (field.stonePartOfMill[i] != compareField->stonePartOfMill[i]) { cout << "error - stonePart[] differs!" << endl; ret = false; }
for (j=0; j<4; j++) {
if (field.connectedSquare[i][j] != compareField->connectedSquare[i][j]) { cout << "error - connectedSquare[] differs!" << endl; ret = false; }
// if (field.stoneMoveAble[i][j] != compareField->stoneMoveAble[i][j]) { cout << "error - stoneMoveAble differs!" << endl; ret = false; }
if (field.neighbour[i][j/2][j%2]!= compareField->neighbour[i][j/2][j%2]){ cout << "error - neighbour differs!" << endl; ret = false; }
}}
return ret;
}
//-----------------------------------------------------------------------------
// Name: comparePlayers()
// Desc: Compares the two passed players and returns false if they differ.
//-----------------------------------------------------------------------------
bool muehle::comparePlayers(playerStruct *playerA, playerStruct *playerB)
{
// unsigned int i;
bool ret = true;
if (playerA->numStonesMissing != playerB->numStonesMissing) { cout << "error - numStonesMissing differs!" << endl; ret = false; }
if (playerA->numStones != playerB->numStones) { cout << "error - numStones differs!" << endl; ret = false; }
if (playerA->id != playerB->id) { cout << "error - id differs!" << endl; ret = false; }
if (playerA->warning != playerB->warning) { cout << "error - warning differs!" << endl; ret = false; }
if (playerA->numPossibleMoves != playerB->numPossibleMoves) { cout << "error - numPossibleMoves differs!" << endl; ret = false; }
// for (i=0; i<MAX_NUM_POS_MOVES; i++) if (playerA->posFrom[i] = playerB->posFrom[i]) return false;
// for (i=0; i<MAX_NUM_POS_MOVES; i++) if (playerA->posTo [i] = playerB->posTo [i]) return false;
return ret;
}
//-----------------------------------------------------------------------------
// Name: printField()
// Desc: Calls the printField() function of the current field.
// Prints the current game state on the screen.
//-----------------------------------------------------------------------------
void muehle::printField()
{
field.printField();
}
//-----------------------------------------------------------------------------
// Name: undoLastMove()
// Desc: Sets the initial field as the current one and apply all (minus one) moves from the move history.
//-----------------------------------------------------------------------------
void muehle::undoLastMove(void)
{
// locals
unsigned int *moveLogFrom_bak = new unsigned int[movesDone];
unsigned int *moveLogTo_bak = new unsigned int[movesDone];
unsigned int movesDone_bak = movesDone;
unsigned int i;
// at least one move must be done
if (movesDone) {
// make backup of log
for (i=0; i<movesDone; i++) {
moveLogFrom_bak[i] = moveLogFrom[i];
moveLogTo_bak[i] = moveLogTo[i];
}
// reset
initialField.copyField(&field);
winner = 0;
movesDone = 0;
// and play again
for (i=0; i<movesDone_bak-1; i++) {
moveStone(moveLogFrom_bak[i], moveLogTo_bak[i]);
}
}
// free mem
delete [] moveLogFrom_bak;
delete [] moveLogTo_bak;
}
//-----------------------------------------------------------------------------
// Name: calcNumberOfRestingStones()
// Desc:
//-----------------------------------------------------------------------------
void muehle::calcNumberOfRestingStones(int &numWhiteStonesResting, int &numBlackStonesResting)
{
if (getCurrentPlayer() == fieldStruct::playerTwo) {
numWhiteStonesResting = fieldStruct::numStonesPerPlayer - field.curPlayer->numStonesMissing - field.curPlayer->numStones;
numBlackStonesResting = fieldStruct::numStonesPerPlayer - field.oppPlayer->numStonesMissing - field.oppPlayer->numStones;
} else {
numWhiteStonesResting = fieldStruct::numStonesPerPlayer - field.oppPlayer->numStonesMissing - field.oppPlayer->numStones;
numBlackStonesResting = fieldStruct::numStonesPerPlayer - field.curPlayer->numStonesMissing - field.curPlayer->numStones;
}
}

90
src/perfect/muehle.h Normal file
View File

@ -0,0 +1,90 @@
/*********************************************************************\
muehle.h
Copyright (c) Thomas Weber. All rights reserved.
Licensed under the MIT License.
https://github.com/madweasel/madweasels-cpp
\*********************************************************************/
#ifndef MUEHLE_H
#define MUEHLE_H
#include <iostream>
#include <cstdio>
#include <time.h>
#include <stdlib.h>
#include "muehleKI.h"
using namespace std;
/*** Konstanten ******************************************************/
#define MAX_NUM_MOVES 10000
/*** Makros ******************************************************/
#define SAFE_DELETE(p) { if(p) { delete (p); (p)=NULL; } }
#define SAFE_DELETE_ARRAY(p) { if(p) { delete[] (p); (p)=NULL; } }
/*** Klassen *********************************************************/
class muehle
{
private:
// Variables
unsigned int *moveLogFrom, *moveLogTo, movesDone; // array containing the history of moves done
muehleKI *playerOneKI; // class-pointer to the AI of player one
muehleKI *playerTwoKI; // class-pointer to the AI of player two
fieldStruct field; // current field
fieldStruct initialField; // undo of the last move is done by setting the initial field und performing all moves saved in history
int winner; // playerId of the player who has won the game. zero if game is still running.
int beginningPlayer; // playerId of the player who makes the first move
// Functions
void deleteArrays ();
void setNextPlayer ();
void calcPossibleMoves (playerStruct *player);
void updateMillsAndWarnings (unsigned int newStone);
bool isNormalMovePossible (unsigned int from, unsigned int to, playerStruct *player);
void setWarningAndMill (unsigned int stone, unsigned int firstNeighbour, unsigned int secondNeighbour, bool isNewStone);
public:
// Constructor / destructor
muehle ();
~muehle ();
// Functions
void undoLastMove ();
void beginNewGame (muehleKI *firstPlayerKI, muehleKI *secondPlayerKI, int currentPlayer);
void setKI (int player, muehleKI *KI);
bool moveStone (unsigned int pushFrom, unsigned int pushTo);
void getComputersChoice (unsigned int *pushFrom, unsigned int *pushTo);
bool setCurrentGameState (fieldStruct *curState);
bool compareWithField (fieldStruct *compareField);
bool comparePlayers (playerStruct *playerA, playerStruct *playerB);
void printField ();
bool startSettingPhase (muehleKI *firstPlayerKI, muehleKI *secondPlayerKI, int currentPlayer, bool settingPhase);
bool putStone (unsigned int pos, int player);
bool settingPhaseHasFinished ();
void getChoiceOfSpecialKI (muehleKI *KI, unsigned int *pushFrom, unsigned int *pushTo);
void setUpCalcPossibleMoves (playerStruct *player);
void setUpSetWarningAndMill (unsigned int stone, unsigned int firstNeighbour, unsigned int secondNeighbour);
void calcNumberOfRestingStones (int &numWhiteStonesResting, int &numBlackStonesResting);
// getter
void getLog (unsigned int &numMovesDone, unsigned int *from, unsigned int *to);
bool getField (int *pField);
bool isCurrentPlayerHuman ();
bool isOpponentPlayerHuman ();
bool inSettingPhase () { return field.settingPhase; }
unsigned int mustStoneBeRemoved () { return field.stoneMustBeRemoved; }
int getWinner () { return winner; }
int getCurrentPlayer () { return field.curPlayer->id; }
unsigned int getLastMoveFrom () { return (movesDone ? moveLogFrom[movesDone-1] : field.size); }
unsigned int getLastMoveTo () { return (movesDone ? moveLogTo [movesDone-1] : field.size); }
unsigned int getMovesDone () { return movesDone; }
unsigned int getNumStonesSet () { return field.stonesSet; }
int getBeginningPlayer () { return beginningPlayer; }
unsigned int getNumStonOfCurPlayer () { return field.curPlayer->numStones; }
unsigned int getNumStonOfOppPlayer () { return field.oppPlayer->numStones; }
};
#endif

235
src/perfect/muehleKI.cpp Normal file
View File

@ -0,0 +1,235 @@
/*********************************************************************
muehleKI.cpp
Copyright (c) Thomas Weber. All rights reserved.
Licensed under the MIT License.
https://github.com/madweasel/madweasels-cpp
\*********************************************************************/
#include "muehleKI.h"
using namespace std;
//-----------------------------------------------------------------------------
// Name: printField()
// Desc:
//-----------------------------------------------------------------------------
void fieldStruct::printField()
{
// locals
unsigned int index;
char c[fieldStruct::size];
for (index=0; index<fieldStruct::size; index++) c[index] = GetCharFromStone(this->field[index]);
cout << "current player : " << GetCharFromStone(this->curPlayer->id) << " has " << this->curPlayer->numStones << " stones\n";
cout << "opponent player : " << GetCharFromStone(this->oppPlayer->id) << " has " << this->oppPlayer->numStones << " stones\n";
cout << "Num Stones to be removed: " << this->stoneMustBeRemoved << "\n";
cout << "setting phase : " << (this->settingPhase ? "true" : "false");
cout << "\n";
cout << "\n a-----b-----c " << c[0] << "-----" << c[1] << "-----" << c[2];
cout << "\n | | | " << "| | |";
cout << "\n | d---e---f | " << "| " << c[3] << "---" << c[4] << "---" << c[5] << " |";
cout << "\n | | | | | " << "| | | | |";
cout << "\n | | g-h-i | | " << "| | " << c[6] << "-" << c[7] << "-" << c[8] << " | |";
cout << "\n | | | | | | | " << "| | | | | |";
cout << "\n j-k-l m-n-o " << c[9] << "-" << c[10] << "-" << c[11] << " " << c[12] << "-" << c[13] << "-" << c[14];
cout << "\n | | | | | | | " << "| | | | | |";
cout << "\n | | p-q-r | | " << "| | " << c[15] << "-" << c[16] << "-" << c[17] << " | |";
cout << "\n | | | | | " << "| | | | |";
cout << "\n | s---t---u | " << "| " << c[18] << "---" << c[19] << "---" << c[20] << " |";
cout << "\n | | | " << "| | |";
cout << "\n v-----w-----x " << c[21] << "-----" << c[22] << "-----" << c[23];
cout << "\n";
}
//-----------------------------------------------------------------------------
// Name: GetCharFromStone()
// Desc:
//-----------------------------------------------------------------------------
char fieldStruct::GetCharFromStone(int stone)
{
switch (stone)
{
case fieldStruct::playerOne: return 'o';
case fieldStruct::playerTwo: return 'x';
case fieldStruct::playerOneWarning: return '1';
case fieldStruct::playerTwoWarning: return '2';
case fieldStruct::playerBothWarning: return '3';
case fieldStruct::squareIsFree: return ' ';
}
return 'f';
}
//-----------------------------------------------------------------------------
// Name: copyField()
// Desc: Only copies the values without array creation.
//-----------------------------------------------------------------------------
void fieldStruct::copyField(fieldStruct *destination)
{
unsigned int i, j;
this->curPlayer->copyPlayer(destination->curPlayer);
this->oppPlayer->copyPlayer(destination->oppPlayer);
destination->stonesSet = this->stonesSet;
destination->settingPhase = this->settingPhase;
destination->stoneMustBeRemoved = this->stoneMustBeRemoved;
for (i=0; i<this->size; i++) {
destination->field[i] = this->field[i];
destination->warnings[i] = this->warnings[i];
destination->stonePartOfMill[i] = this->stonePartOfMill[i];
for (j=0; j<4; j++) {
destination->connectedSquare[i][j] = this->connectedSquare[i][j];
destination->stoneMoveAble[i][j] = this->stoneMoveAble[i][j];
destination->neighbour[i][j/2][j%2] = this->neighbour[i][j/2][j%2];
}}
}
//-----------------------------------------------------------------------------
// Name: copyPlayer()
// Desc: Only copies the values without array creation.
//-----------------------------------------------------------------------------
void playerStruct::copyPlayer(playerStruct *destination)
{
unsigned int i;
destination->numStonesMissing = this->numStonesMissing;
destination->numStones = this->numStones;
destination->id = this->id;
destination->warning = this->warning;
destination->numPossibleMoves = this->numPossibleMoves;
for (i=0; i<MAX_NUM_POS_MOVES; i++) destination->posFrom[i] = this->posFrom[i];
for (i=0; i<MAX_NUM_POS_MOVES; i++) destination->posTo [i] = this->posTo [i];
}
//-----------------------------------------------------------------------------
// Name: createField()
// Desc: Creates, but doesn't initialize, the arrays of the of the passed field structure.
//-----------------------------------------------------------------------------
void fieldStruct::createField()
{
// locals
unsigned int i;
curPlayer = new playerStruct;
oppPlayer = new playerStruct;
curPlayer->id = playerOne;
stonesSet = 0;
stoneMustBeRemoved = 0;
settingPhase = true;
curPlayer->warning = (curPlayer->id == playerOne) ? playerOneWarning : playerTwoWarning;
oppPlayer->id = (curPlayer->id == playerOne) ? playerTwo : playerOne;
oppPlayer->warning = (curPlayer->id == playerOne) ? playerTwoWarning : playerOneWarning;
curPlayer->numStones = 0;
oppPlayer->numStones = 0;
curPlayer->numPossibleMoves = 0;
oppPlayer->numPossibleMoves = 0;
curPlayer->numStonesMissing = 0;
oppPlayer->numStonesMissing = 0;
// zero
for (i=0; i<size; i++) {
field[i] = squareIsFree;
warnings[i] = noWarning;
stonePartOfMill[i] = 0;
stoneMoveAble[i][0] = false;
stoneMoveAble[i][1] = false;
stoneMoveAble[i][2] = false;
stoneMoveAble[i][3] = false;
}
// set connections
i = size;
setConnection( 0, 1, 9, i, i);
setConnection( 1, 2, 4, 0, i);
setConnection( 2, i, 14, 1, i);
setConnection( 3, 4, 10, i, i);
setConnection( 4, 5, 7, 3, 1);
setConnection( 5, i, 13, 4, i);
setConnection( 6, 7, 11, i, i);
setConnection( 7, 8, i, 6, 4);
setConnection( 8, i, 12, 7, i);
setConnection( 9, 10, 21, i, 0);
setConnection(10, 11, 18, 9, 3);
setConnection(11, i, 15, 10, 6);
setConnection(12, 13, 17, i, 8);
setConnection(13, 14, 20, 12, 5);
setConnection(14, i, 23, 13, 2);
setConnection(15, 16, i, i, 11);
setConnection(16, 17, 19, 15, i);
setConnection(17, i, i, 16, 12);
setConnection(18, 19, i, i, 10);
setConnection(19, 20, 22, 18, 16);
setConnection(20, i, i, 19, 13);
setConnection(21, 22, i, i, 9);
setConnection(22, 23, i, 21, 19);
setConnection(23, i, i, 22, 14);
// neighbours
setNeighbour( 0, 1, 2, 9, 21);
setNeighbour( 1, 0, 2, 4, 7);
setNeighbour( 2, 0, 1, 14, 23);
setNeighbour( 3, 4, 5, 10, 18);
setNeighbour( 4, 1, 7, 3, 5);
setNeighbour( 5, 3, 4, 13, 20);
setNeighbour( 6, 7, 8, 11, 15);
setNeighbour( 7, 1, 4, 6, 8);
setNeighbour( 8, 6, 7, 12, 17);
setNeighbour( 9, 10, 11, 0, 21);
setNeighbour( 10, 9, 11, 3, 18);
setNeighbour( 11, 9, 10, 6, 15);
setNeighbour( 12, 13, 14, 8, 17);
setNeighbour( 13, 12, 14, 5, 20);
setNeighbour( 14, 12, 13, 2, 23);
setNeighbour( 15, 6, 11, 16, 17);
setNeighbour( 16, 15, 17, 19, 22);
setNeighbour( 17, 15, 16, 8, 12);
setNeighbour( 18, 3, 10, 19, 20);
setNeighbour( 19, 18, 20, 16, 22);
setNeighbour( 20, 5, 13, 18, 19);
setNeighbour( 21, 0, 9, 22, 23);
setNeighbour( 22, 16, 19, 21, 23);
setNeighbour( 23, 2, 14, 21, 22);
}
//-----------------------------------------------------------------------------
// Name: deleteField()
// Desc: ...
//-----------------------------------------------------------------------------
void fieldStruct::deleteField()
{
SAFE_DELETE(curPlayer);
SAFE_DELETE(oppPlayer);
}
//-----------------------------------------------------------------------------
// Name: setConnection()
// Desc:
//-----------------------------------------------------------------------------
inline void fieldStruct::setConnection(unsigned int index, int firstDirection, int secondDirection, int thirdDirection, int fourthDirection)
{
connectedSquare[index][0] = firstDirection;
connectedSquare[index][1] = secondDirection;
connectedSquare[index][2] = thirdDirection;
connectedSquare[index][3] = fourthDirection;
}
//-----------------------------------------------------------------------------
// Name: setNeighbour()
// Desc:
//-----------------------------------------------------------------------------
inline void fieldStruct::setNeighbour(unsigned int index, unsigned int firstNeighbour0, unsigned int secondNeighbour0, unsigned int firstNeighbour1, unsigned int secondNeighbour1)
{
neighbour[index][0][0] = firstNeighbour0;
neighbour[index][0][1] = secondNeighbour0;
neighbour[index][1][0] = firstNeighbour1;
neighbour[index][1][1] = secondNeighbour1;
}

92
src/perfect/muehleKI.h Normal file
View File

@ -0,0 +1,92 @@
/*********************************************************************\
muehleKI.h
Copyright (c) Thomas Weber. All rights reserved.
Licensed under the MIT License.
https://github.com/madweasel/madweasels-cpp
\*********************************************************************/
#ifndef MUEHLE_KI_H
#define MUEHLE_KI_H
#include <iostream>
#include <cstdio>
//using namespace std;
/*** Konstanten ******************************************************/
#define MAX_NUM_POS_MOVES (3 * 18) // not (9 * 4) = 36 since the possibilities with 3 stones are more
#define SAFE_DELETE(p) { if(p) { delete (p); (p)=NULL; } }
/*** Klassen *********************************************************/
class playerStruct
{
public:
int id; // static
unsigned int warning; // static
unsigned int numStones; // number of stones of this player on the field
unsigned int numStonesMissing; // number of stones, which where stolen by the opponent
unsigned int numPossibleMoves; // amount of possible moves
unsigned int posTo [MAX_NUM_POS_MOVES]; // target field position of a possible move
unsigned int posFrom[MAX_NUM_POS_MOVES]; // source field position of a possible move
void copyPlayer (playerStruct *destination);
};
class fieldStruct
{
public:
// constants
static const int squareIsFree = 0; // trivial
static const int playerOne = -1; // so rowOwner can be calculated easy
static const int playerTwo = 1;
static const int playerBlack = -1; // so rowOwner can be calculated easy
static const int playerWhite = 1;
static const unsigned int noWarning = 0; // so the bitwise or-operation can be applied, without interacting with playerOne & Two
static const unsigned int playerOneWarning = 2;
static const unsigned int playerTwoWarning = 4;
static const unsigned int playerBothWarning = 6;
static const unsigned int numStonesPerPlayer = 9;
static const unsigned int size = 24; // number of squares
static const int gameDrawn = 3; // only a nonzero value
// variables
int field[size]; // one of the values above for each field position
unsigned int warnings[size]; // array containing the warnings for each field position
bool stoneMoveAble[size][4]; // true if stone can be moved in this direction
unsigned int stonePartOfMill[size]; // the number of mills, of which this stone is part of
unsigned int connectedSquare[size][4]; // static array containg the index of the neighbour or "size"
unsigned int neighbour[size][2][2]; // static array containing the two neighbours of each squares
unsigned int stonesSet; // number of stones set in the setting phase
bool settingPhase; // true if stonesSet < 18
unsigned int stoneMustBeRemoved; // number of stones which must be removed by the current player
playerStruct *curPlayer, *oppPlayer; // pointers to the current and opponent player
// useful functions
void printField ();
void copyField (fieldStruct *destination);
void createField ();
void deleteField ();
private:
// helper functions
char GetCharFromStone (int stone);
void setConnection (unsigned int index, int firstDirection, int secondDirection, int thirdDirection, int fourthDirection);
void setNeighbour (unsigned int index, unsigned int firstNeighbour0, unsigned int secondNeighbour0, unsigned int firstNeighbour1, unsigned int secondNeighbour1);
};
class muehleKI abstract
{
protected:
fieldStruct dummyField;
public:
// Constructor / destructor
muehleKI() { dummyField.createField(); };
~muehleKI() { dummyField.deleteField(); };
// Functions
virtual void play (fieldStruct *theField, unsigned int *pushFrom, unsigned int *pushTo) = 0;
};
#endif

176
src/perfect/perfect.vcxproj Normal file
View File

@ -0,0 +1,176 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
<ProjectGuid>{edb1e279-1476-443b-84fa-150a5d3b5a10}</ProjectGuid>
<RootNamespace>perfect</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="bufferedFile.h" />
<ClInclude Include="cyclicArray.h" />
<ClInclude Include="miniMax.h" />
<ClInclude Include="miniMaxWin.h" />
<ClInclude Include="miniMax_retroAnalysis.h" />
<ClInclude Include="minMaxKI.h" />
<ClInclude Include="muehle.h" />
<ClInclude Include="muehleKI.h" />
<ClInclude Include="perfectKI.h" />
<ClInclude Include="randomKI.h" />
<ClInclude Include="strLib.h" />
<ClInclude Include="threadManager.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="bufferedFile.cpp" />
<ClCompile Include="console.cpp" />
<ClCompile Include="cyclicArray.cpp" />
<ClCompile Include="miniMax.cpp" />
<ClCompile Include="miniMax_alphaBetaAlgorithmn.cpp" />
<ClCompile Include="miniMax_database.cpp" />
<ClCompile Include="miniMax_retroAnalysis.cpp" />
<ClCompile Include="miniMax_statistics.cpp" />
<ClCompile Include="miniMax_test.cpp" />
<ClCompile Include="minMaxKI.cpp" />
<ClCompile Include="muehle.cpp" />
<ClCompile Include="muehleKI.cpp" />
<ClCompile Include="perfectKI.cpp" />
<ClCompile Include="randomKI.cpp" />
<ClCompile Include="strLib.cpp" />
<ClCompile Include="threadManager.cpp" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@ -0,0 +1,105 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="bufferedFile.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="cyclicArray.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="minMaxKI.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="muehle.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="muehleKI.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="perfectKI.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="randomKI.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="strLib.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="threadManager.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="miniMax.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="miniMax_retroAnalysis.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="miniMaxWin.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="bufferedFile.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="console.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="cyclicArray.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="minMaxKI.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="muehle.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="muehleKI.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="perfectKI.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="randomKI.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="strLib.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="threadManager.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="miniMax.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="miniMax_alphaBetaAlgorithmn.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="miniMax_database.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="miniMax_retroAnalysis.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="miniMax_statistics.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="miniMax_test.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>

2542
src/perfect/perfectKI.cpp Normal file

File diff suppressed because it is too large Load Diff

242
src/perfect/perfectKI.h Normal file
View File

@ -0,0 +1,242 @@
/*********************************************************************\
perfectKI.h
Copyright (c) Thomas Weber. All rights reserved.
Licensed under the MIT License.
https://github.com/madweasel/madweasels-cpp
\*********************************************************************/
#ifndef PERFEKT_KI_H
#define PERFEKT_KI_H
#include <iostream>
#include <cstdio>
#include <math.h>
#include "muehleKI.h"
#include "miniMax.h"
//using namespace std;
// values of states/situations
#define VALUE_GAME_LOST -1000.0f
#define VALUE_GAME_WON 1000.0f
// since a state must be saved two times,
// one time where no stone must be removed,
// one time where a stone must be removed
#define MAX_NUM_STONES_REMOVED_MINUS_1 2
// 10 x 10 since each color can range from 0 to 9 stones
// x2 since there is the setting phase and the moving phase
#define NUM_LAYERS 200
#define MAX_NUM_SUB_LAYERS 100
#define LAYER_INDEX_SETTING_PHASE 1
#define LAYER_INDEX_MOVING_PHASE 0
#define NOT_INDEXED 4294967295
#define MAX_DEPTH_OF_TREE 100
#define NUM_STONES_PER_PLAYER 9
#define NUM_STONES_PER_PLAYER_PLUS_ONE 10
// The Four Groups (the field position is divided in four groups A,B,C,D)
#define numSquaresGroupA 4
#define numSquaresGroupB 4
#define numSquaresGroupC 8
#define numSquaresGroupD 8
#define GROUP_A 0
#define GROUP_B 1
#define GROUP_C 2
#define GROUP_D 3
#define MAX_ANZ_STELLUNGEN_A 81
#define MAX_ANZ_STELLUNGEN_B 81
#define MAX_ANZ_STELLUNGEN_C (81*81)
#define MAX_ANZ_STELLUNGEN_D (81*81)
#define FREE_SQUARE 0
#define WHITE_STONE 1
#define BLACK_STONE 2
// Symmetry Operations
#define SO_TURN_LEFT 0
#define SO_TURN_180 1
#define SO_TURN_RIGHT 2
#define SO_DO_NOTHING 3
#define SO_INVERT 4
#define SO_MIRROR_VERT 5
#define SO_MIRROR_HORI 6
#define SO_MIRROR_DIAG_1 7
#define SO_MIRROR_DIAG_2 8
#define SO_INV_LEFT 9
#define SO_INV_RIGHT 10
#define SO_INV_180 11
#define SO_INV_MIR_VERT 12
#define SO_INV_MIR_HORI 13
#define SO_INV_MIR_DIAG_1 14
#define SO_INV_MIR_DIAG_2 15
#define NUM_SYM_OPERATIONS 16
/*** Klassen *********************************************************/
class perfectKI : public muehleKI, public miniMax
{
protected:
// structs
struct subLayerStruct
{
unsigned int minIndex;
unsigned int maxIndex;
unsigned int numWhiteStonesGroupCD, numBlackStonesGroupCD;
unsigned int numWhiteStonesGroupAB, numBlackStonesGroupAB;
};
struct layerStruct
{
unsigned int numWhiteStones;
unsigned int numBlackStones;
unsigned int numSubLayers;
unsigned int subLayerIndexAB[NUM_STONES_PER_PLAYER_PLUS_ONE][NUM_STONES_PER_PLAYER_PLUS_ONE];
unsigned int subLayerIndexCD[NUM_STONES_PER_PLAYER_PLUS_ONE][NUM_STONES_PER_PLAYER_PLUS_ONE];
subLayerStruct subLayer[MAX_NUM_SUB_LAYERS];
};
struct possibilityStruct
{
unsigned int from[MAX_NUM_POS_MOVES];
unsigned int to [MAX_NUM_POS_MOVES];
};
struct backupStruct
{
float floatValue;
twoBit shortValue;
bool gameHasFinished;
bool settingPhase;
int fieldFrom, fieldTo; // value of field
unsigned int from, to; // index of field
unsigned int curNumStones, oppNumStones;
unsigned int curPosMoves, oppPosMoves;
unsigned int curMissStones, oppMissStones;
unsigned int stonesSet;
unsigned int stoneMustBeRemoved;
unsigned int stonePartOfMill[fieldStruct::size];
playerStruct *curPlayer, *oppPlayer;
};
// preCalcedVars.dat
struct preCalcedVarsFileHeaderStruct
{
unsigned int sizeInBytes;
};
// constant variables for state addressing in the database
layerStruct layer [NUM_LAYERS]; // the layers
unsigned int layerIndex [2][NUM_STONES_PER_PLAYER_PLUS_ONE][NUM_STONES_PER_PLAYER_PLUS_ONE]; // indices of layer [moving/setting phase][number of white stones][number of black stones]
unsigned int anzahlStellungenCD [NUM_STONES_PER_PLAYER_PLUS_ONE][NUM_STONES_PER_PLAYER_PLUS_ONE];
unsigned int anzahlStellungenAB [NUM_STONES_PER_PLAYER_PLUS_ONE][NUM_STONES_PER_PLAYER_PLUS_ONE];
unsigned int indexAB [MAX_ANZ_STELLUNGEN_A*MAX_ANZ_STELLUNGEN_B];
unsigned int indexCD [MAX_ANZ_STELLUNGEN_C*MAX_ANZ_STELLUNGEN_D];
unsigned char symmetryOperationCD [MAX_ANZ_STELLUNGEN_C*MAX_ANZ_STELLUNGEN_D]; // index of symmetry operation used to get from the original state to the current one
unsigned int powerOfThree [numSquaresGroupC+numSquaresGroupD]; // 3^0, 3^1, 3^2, ...
unsigned int symmetryOperationTable [NUM_SYM_OPERATIONS][fieldStruct::size]; // Matrix used for application of the symmetry operations
unsigned int * originalStateCD [NUM_STONES_PER_PLAYER_PLUS_ONE][NUM_STONES_PER_PLAYER_PLUS_ONE];
unsigned int * originalStateAB [NUM_STONES_PER_PLAYER_PLUS_ONE][NUM_STONES_PER_PLAYER_PLUS_ONE];
unsigned int reverseSymOperation [NUM_SYM_OPERATIONS]; // index of the reverse symmetry operation
unsigned int concSymOperation [NUM_SYM_OPERATIONS][NUM_SYM_OPERATIONS]; // symmetry operation, which is identical to applying those two in the index
unsigned int mOverN [fieldStruct::size+1][fieldStruct::size+1]; // m over n
unsigned char valueOfMove [fieldStruct::size * fieldStruct::size]; // contains the value of the situation, which will be achieved by that move
unsigned short plyInfoForOutput [fieldStruct::size * fieldStruct::size]; // contains the value of the situation, which will be achieved by that move
unsigned int incidencesValuesSubMoves [fieldStruct::size * fieldStruct::size][4]; // contains the number of ...
unsigned int symmetricStateNumberArray [NUM_SYM_OPERATIONS]; // array for state numbers
string databaseDirectory; // directory containing the database files
// Variables used individually by each single thread
class threadVarsStruct
{
public:
fieldStruct * field; // pointer of the current field [changed by move()]
float floatValue; // value of current situation for field->currentPlayer
twoBit shortValue; // ''
bool gameHasFinished; // someone has won or current field is full
int ownId; // id of the player who called the play()-function
unsigned int curSearchDepth; // current level
unsigned int depthOfFullTree; // search depth where the whole tree is explored
unsigned int * idPossibilities; // returned pointer of getPossibilities()-function
backupStruct * oldStates; // for undo()-function
possibilityStruct * possibilities; // for getPossNormalMove()-function
perfectKI * parent; //
// constructor
threadVarsStruct ();
// Functions
unsigned int * getPossSettingPhase (unsigned int *numPossibilities, void **pPossibilities);
unsigned int * getPossNormalMove (unsigned int *numPossibilities, void **pPossibilities);
unsigned int * getPossStoneRemove (unsigned int *numPossibilities, void **pPossibilities);
// move functions
inline void updatePossibleMoves (unsigned int stone, playerStruct *stoneOwner, bool stoneRemoved, unsigned int ignoreStone);
inline void updateWarning (unsigned int firstStone, unsigned int secondStone);
inline void setWarning (unsigned int stoneOne, unsigned int stoneTwo, unsigned int stoneThree);
inline void removeStone (unsigned int from, backupStruct *backup);
inline void setStone (unsigned int to, backupStruct *backup);
inline void normalMove (unsigned int from, unsigned int to, backupStruct *backup);
// database functions
unsigned int getLayerAndStateNumber (unsigned int &layerNum, unsigned int &stateNumber);
void setWarningAndMill (unsigned int stone, unsigned int firstNeighbour, unsigned int secondNeighbour);
bool fieldIntegrityOK (unsigned int numberOfMillsCurrentPlayer, unsigned int numberOfMillsOpponentPlayer, bool aStoneCanBeRemovedFromCurPlayer);
void calcPossibleMoves (playerStruct *player);
void storePredecessor (unsigned int numberOfMillsCurrentPlayer, unsigned int numberOfMillsOpponentPlayer, unsigned int *amountOfPred, retroAnalysisPredVars *predVars);
};
threadVarsStruct * threadVars;
// database functions
unsigned int getNumberOfLayers ();
unsigned int getNumberOfKnotsInLayer (unsigned int layerNum);
long long mOverN_Function (unsigned int m, unsigned int n);
void applySymmetrieOperationOnField (unsigned char symmetryOperationNumber, unsigned int *sourceField, unsigned int *destField);
bool isSymOperationInvariantOnGroupCD(unsigned int symmetryOperation, int *theField);
bool shallRetroAnalysisBeUsed (unsigned int layerNum);
void getSuccLayers (unsigned int layerNum, unsigned int *amountOfSuccLayers, unsigned int *succLayers);
void getPredecessors (unsigned int threadNo, unsigned int *amountOfPred, retroAnalysisPredVars *predVars);
bool setSituation (unsigned int threadNo, unsigned int layerNum, unsigned int stateNumber);
unsigned int getLayerNumber (unsigned int threadNo);
unsigned int getLayerAndStateNumber (unsigned int threadNo, unsigned int &layerNum, unsigned int &stateNumber);
// integrity test functions
bool checkMoveAndSetSituation ();
bool checkGetPossThanGetPred ();
bool checkGetPredThanGetPoss ();
// Virtual Functions
void prepareBestChoiceCalculation ();
void getValueOfSituation (unsigned int threadNo, float &floatValue, twoBit &shortValue);
void setOpponentLevel (unsigned int threadNo, bool isOpponentLevel);
bool getOpponentLevel (unsigned int threadNo);
void deletePossibilities (unsigned int threadNo, void *pPossibilities);
unsigned int * getPossibilities (unsigned int threadNo, unsigned int *numPossibilities, bool *opponentsMove, void **pPossibilities);
void undo (unsigned int threadNo, unsigned int idPossibility, bool opponentsMove, void *pBackup, void *pPossibilities);
void move (unsigned int threadNo, unsigned int idPossibility, bool opponentsMove, void **pBackup, void *pPossibilities);
void printMoveInformation (unsigned int threadNo, unsigned int idPossibility, void *pPossibilities);
void storeValueOfMove (unsigned int threadNo, unsigned int idPossibility, void *pPossibilities, unsigned char value, unsigned int *freqValuesSubMoves, plyInfoVarType plyInfo);
void getSymStateNumWithDoubles (unsigned int threadNo, unsigned int *numSymmetricStates, unsigned int **symStateNumbers);
void printField (unsigned int threadNo, unsigned char value);
string getOutputInformation (unsigned int layerNum);
unsigned int getPartnerLayer (unsigned int layerNum);
void prepareDatabaseCalculation ();
void wrapUpDatabaseCalculation (bool calculationAborted);
public:
// Constructor / destructor
perfectKI (const char *directory);
~perfectKI ();
// Functions
bool setDatabasePath (const char *directory);
void play (fieldStruct *theField, unsigned int *pushFrom, unsigned int *pushTo);
void getValueOfMoves (unsigned char *moveValue, unsigned int *freqValuesSubMoves, plyInfoVarType *plyInfo, unsigned int *moveQuality, unsigned char &knotValue, plyInfoVarType &bestAmountOfPlies);
void getField (unsigned int layerNum, unsigned int stateNumber, fieldStruct *field, bool *gameHasFinished);
void getLayerAndStateNumber (unsigned int& layerNum, unsigned int& stateNumber);
// Testing functions
bool testLayers (unsigned int startTestFromLayer, unsigned int endTestAtLayer);
};
#endif

86
src/perfect/randomKI.cpp Normal file
View File

@ -0,0 +1,86 @@
/*********************************************************************
randomKI.cpp
Copyright (c) Thomas Weber. All rights reserved.
Licensed under the MIT License.
https://github.com/madweasel/madweasels-cpp
\*********************************************************************/
#include "randomKI.h"
//-----------------------------------------------------------------------------
// Name: randomKI()
// Desc: randomKI class constructor
//-----------------------------------------------------------------------------
randomKI::randomKI()
{
// Init
srand( (unsigned)time( NULL ) );
}
//-----------------------------------------------------------------------------
// Name: ~randomKI()
// Desc: randomKI class destructor
//-----------------------------------------------------------------------------
randomKI::~randomKI()
{
// Locals
}
//-----------------------------------------------------------------------------
// Name: play()
// Desc:
//-----------------------------------------------------------------------------
void randomKI::play(fieldStruct *theField, unsigned int *pushFrom, unsigned int *pushTo)
{
// locals
unsigned int from, to, direction;
bool allowedToSpring = (theField->curPlayer->numStones == 3) ? true : false;
// must stone be removed ?
if (theField->stoneMustBeRemoved) {
// search a stone from the enemy
do {
from = rand() % theField->size;
to = theField->size;
} while (theField->field[from] != theField->oppPlayer->id || theField->stonePartOfMill[from]);
// still in setting phase ?
} else if (theField->settingPhase) {
// search a free square
do {
from = theField->size;
to = rand() % theField->size;
} while (theField->field[to] != theField->squareIsFree);
// try to push randomly
} else {
do {
// search an own stone
do {
from = rand() % theField->size;
} while (theField->field[from] != theField->curPlayer->id);
// select a free square
if (allowedToSpring) {
do {
to = rand() % theField->size;
} while (theField->field[to] != theField->squareIsFree);
// select a connected square
} else {
do {
direction = rand() % 4;
to = theField->connectedSquare[from][direction];
} while (to == theField->size);
}
} while (theField->field[to] != theField->squareIsFree);
}
*pushFrom = from;
*pushTo = to;
}

28
src/perfect/randomKI.h Normal file
View File

@ -0,0 +1,28 @@
/*********************************************************************\
randomKI.h
Copyright (c) Thomas Weber. All rights reserved.
Licensed under the MIT License.
https://github.com/madweasel/madweasels-cpp
\*********************************************************************/
#ifndef RANDOM_KI_H
#define RANDOM_KI_H
#include <stdlib.h>
#include <time.h>
#include "muehleKI.h"
/*** Klassen *********************************************************/
class randomKI : public muehleKI
{
public:
// Constructor / destructor
randomKI();
~randomKI();
// Functions
void play(fieldStruct *theField, unsigned int *pushFrom, unsigned int *pushTo);
};
#endif

250
src/perfect/strLib.cpp Normal file
View File

@ -0,0 +1,250 @@
/*********************************************************************
strLib.cpp
Copyright (c) Thomas Weber. All rights reserved.
Licensed under the MIT License.
https://github.com/madweasel/madweasels-cpp
\*********************************************************************/
#include "strLib.h"
//-----------------------------------------------------------------------------
// Name: hibit()
// Desc:
//-----------------------------------------------------------------------------
int mystring::hibit(unsigned int n)
{
n |= (n >> 1);
n |= (n >> 2);
n |= (n >> 4);
n |= (n >> 8);
n |= (n >> 16);
return n - (n >> 1);
}
//-----------------------------------------------------------------------------
// Name: mystring()
// Desc:
//-----------------------------------------------------------------------------
mystring::mystring()
{
}
//-----------------------------------------------------------------------------
// Name: mystring()
// Desc:
//-----------------------------------------------------------------------------
mystring::mystring(const char *cStr)
{
assign(cStr);
}
//-----------------------------------------------------------------------------
// Name: mystring()
// Desc:
//-----------------------------------------------------------------------------
mystring::mystring(const WCHAR *cStr)
{
assign(cStr);
}
//-----------------------------------------------------------------------------
// Name: mystring()
// Desc:
//-----------------------------------------------------------------------------
mystring::~mystring()
{
if (strA != nullptr) { delete [] strA; strA = nullptr; }
if (strW != nullptr) { delete [] strW; strW = nullptr; }
strW = nullptr;
strA = nullptr;
length = 0;
reserved = 0;
}
//-----------------------------------------------------------------------------
// Name: c_strA()
// Desc:
//-----------------------------------------------------------------------------
const char * mystring::c_strA()
{
return strA;
}
//-----------------------------------------------------------------------------
// Name: c_strW()
// Desc:
//-----------------------------------------------------------------------------
const WCHAR * mystring::c_strW()
{
return strW;
}
//-----------------------------------------------------------------------------
// Name: assign()
// Desc:
//-----------------------------------------------------------------------------
mystring & mystring::assign(const char *cStr)
{
// locals
size_t convertedChars = 0;
size_t newLength = strlen(cStr);
size_t newReserved = (size_t) hibit((unsigned int) newLength) * 2;
if (reserved < newReserved) this->~mystring();
if (strA == nullptr) strA = new char [newReserved];
if (strW == nullptr) strW = new WCHAR[newReserved];
reserved = newReserved;
length = newLength;
strcpy_s(strA, newReserved, cStr);
mbstowcs_s(&convertedChars, strW, newLength+1, cStr, _TRUNCATE);
return *this;
}
//-----------------------------------------------------------------------------
// Name: assign()
// Desc:
//-----------------------------------------------------------------------------
mystring & mystring::assign(const WCHAR *cStr)
{
// locals
size_t returnValue;
size_t newLength = wcslen(cStr);
size_t newReserved = (size_t) hibit((unsigned int) newLength) * 2;
if (reserved < newReserved) this->~mystring();
if (strA == nullptr) strA = new char [newReserved];
if (strW == nullptr) strW = new WCHAR[newReserved];
reserved = newReserved;
length = newLength;
wcscpy_s(strW, newReserved, cStr);
wcstombs_s(&returnValue, strA, newLength+1, cStr, newLength+1);
return *this;
}
//-----------------------------------------------------------------------------
// Name: readAsciiData()
// Desc: This functions reads in a table of floating point values faster than "cin".
//-----------------------------------------------------------------------------
bool readAsciiData(HANDLE hFile, double* pData, unsigned int numValues, unsigned char decimalSeperator, unsigned char columnSeparator)
{
// constants
const unsigned int maxValueLengthInBytes = 32;
const unsigned int bufferSize = 1000;
// locals
DWORD dwBytesRead;
unsigned char buffer[bufferSize];
unsigned char * curByte = &buffer[0];
unsigned int curReadValue = 0;
unsigned int actualBufferSize = 0;
unsigned int curBufferPos = bufferSize;
unsigned int decimalPos = 0;
int integralValue = 0; // ACHTUNG: Erlaubt nur 8 Vorkommastellen
int fractionalValue = 0; // ACHTUNG: Erlaubt nur 8 Nachkommastellen
int exponentialValue = 1;
bool valIsNegativ = false;
bool expIsNegativ = false;
bool decimalPlace = false;
bool exponent = false;
double fractionalFactor[] = { 0,
0.1,
0.01,
0.001,
0.0001,
0.00001,
0.000001,
0.0000001,
0.00000001,
0.000000001,
0.0000000001 };
// read each value
do {
// read from buffer if necessary
if (curBufferPos >= bufferSize - maxValueLengthInBytes) {
memcpy(&buffer[0], &buffer[curBufferPos], bufferSize - curBufferPos);
ReadFile(hFile, &buffer[bufferSize - curBufferPos], curBufferPos, &dwBytesRead, nullptr);
actualBufferSize= bufferSize - curBufferPos + dwBytesRead;
curBufferPos = 0;
curByte = &buffer[curBufferPos];
}
// process current byte
switch (*curByte)
{
case '-': if (exponent) { expIsNegativ = true; } else { valIsNegativ = true; } break;
case '+': /* ignore */ break;
case 'e': case 'E': exponent = true; decimalPlace = false; break;
case '0': if (decimalPlace) { fractionalValue *= 10; fractionalValue += 0; decimalPos++; } else if (exponent) { exponentialValue *= 10; exponentialValue += 0; } else { integralValue *= 10; integralValue += 0; } break;
case '1': if (decimalPlace) { fractionalValue *= 10; fractionalValue += 1; decimalPos++; } else if (exponent) { exponentialValue *= 10; exponentialValue += 1; } else { integralValue *= 10; integralValue += 1; } break;
case '2': if (decimalPlace) { fractionalValue *= 10; fractionalValue += 2; decimalPos++; } else if (exponent) { exponentialValue *= 10; exponentialValue += 2; } else { integralValue *= 10; integralValue += 2; } break;
case '3': if (decimalPlace) { fractionalValue *= 10; fractionalValue += 3; decimalPos++; } else if (exponent) { exponentialValue *= 10; exponentialValue += 3; } else { integralValue *= 10; integralValue += 3; } break;
case '4': if (decimalPlace) { fractionalValue *= 10; fractionalValue += 4; decimalPos++; } else if (exponent) { exponentialValue *= 10; exponentialValue += 4; } else { integralValue *= 10; integralValue += 4; } break;
case '5': if (decimalPlace) { fractionalValue *= 10; fractionalValue += 5; decimalPos++; } else if (exponent) { exponentialValue *= 10; exponentialValue += 5; } else { integralValue *= 10; integralValue += 5; } break;
case '6': if (decimalPlace) { fractionalValue *= 10; fractionalValue += 6; decimalPos++; } else if (exponent) { exponentialValue *= 10; exponentialValue += 6; } else { integralValue *= 10; integralValue += 6; } break;
case '7': if (decimalPlace) { fractionalValue *= 10; fractionalValue += 7; decimalPos++; } else if (exponent) { exponentialValue *= 10; exponentialValue += 7; } else { integralValue *= 10; integralValue += 7; } break;
case '8': if (decimalPlace) { fractionalValue *= 10; fractionalValue += 8; decimalPos++; } else if (exponent) { exponentialValue *= 10; exponentialValue += 8; } else { integralValue *= 10; integralValue += 8; } break;
case '9': if (decimalPlace) { fractionalValue *= 10; fractionalValue += 9; decimalPos++; } else if (exponent) { exponentialValue *= 10; exponentialValue += 9; } else { integralValue *= 10; integralValue += 9; } break;
default:
if (*curByte == decimalSeperator) {
decimalPlace = true;
exponent = false;
} else if (*curByte == columnSeparator) {
// everything ok?
if (decimalPos > 8) {
cout << "ERROR in function readAsciiData(): Too many digits on decimal place. Maximum is 8 !" << endl;
return false;
}
// calc final value
(*pData) = integralValue;
if (decimalPos) {
(*pData) += fractionalValue * fractionalFactor[decimalPos];
}
if (valIsNegativ ) {
(*pData) *= -1;
}
if (exponent) {
(*pData) *= pow(10, expIsNegativ ? -1*exponentialValue : 1);
}
// init
valIsNegativ = false;
expIsNegativ = false;
decimalPlace = false;
exponent = false;
integralValue = 0;
fractionalValue = 0;
exponentialValue = 1;
decimalPos = 0;
// save value
pData++;
curReadValue++;
} else {
// do nothing
}
break;
}
// consider next byte
curBufferPos++;
curByte++;
// buffer overrun?
if (curBufferPos >= actualBufferSize) return false;
} while (curReadValue < numValues);
// quit
return true;
}

50
src/perfect/strLib.h Normal file
View File

@ -0,0 +1,50 @@
/*********************************************************************\
strLib.h
Copyright (c) Thomas Weber. All rights reserved.
Licensed under the MIT License.
https://github.com/madweasel/madweasels-cpp
\*********************************************************************/
#ifndef STRLIB_H
#define STRLIB_H
#include <windows.h>
#include <iostream>
#include <stdlib.h>
#include <string>
#include <stdio.h>
#include <assert.h>
using namespace std;
// general functions
bool readAsciiData(HANDLE hFile, double* pData, unsigned int numValues, unsigned char decimalSeperator, unsigned char columnSeparator);
class mystring
{
private:
// variables
WCHAR* strW = nullptr;
char * strA = nullptr;
size_t length = 0;
size_t reserved = 0;
// functions
public:
// functions
mystring ();
mystring (const char *cStr);
mystring (const WCHAR *cStr);
~mystring ();
const char * c_strA ();
const WCHAR * c_strW ();
mystring & assign (const char *cStr);
mystring & assign (const WCHAR *cStr);
static int hibit (unsigned int n);
};
#endif

View File

@ -0,0 +1,404 @@
/*********************************************************************
threadManager.cpp
Copyright (c) Thomas Weber. All rights reserved.
Licensed under the MIT License.
https://github.com/madweasel/madweasels-cpp
\*********************************************************************/
#include "threadManager.h"
//-----------------------------------------------------------------------------
// Name: threadManagerClass()
// Desc: threadManagerClass class constructor
//-----------------------------------------------------------------------------
threadManagerClass::threadManagerClass()
{
// locals
unsigned int curThreadNo;
SYSTEM_INFO m_si = {0};
GetSystemInfo(&m_si);
// init default values
executionPaused = false;
executionCancelled = false;
numThreads = m_si.dwNumberOfProcessors;
hThread = new HANDLE[numThreads];
threadId = new DWORD [numThreads];
hBarrier = new HANDLE[numThreads];
numThreadsPassedBarrier = 0;
InitializeCriticalSection(&csBarrier);
hEventBarrierPassedByEveryBody = CreateEvent(NULL, true, false, NULL);
for (curThreadNo=0; curThreadNo<numThreads; curThreadNo++) {
hThread[curThreadNo] = NULL;
threadId[curThreadNo] = 0;
hBarrier[curThreadNo] = CreateEvent(NULL, false, false, NULL);
}
}
//-----------------------------------------------------------------------------
// Name: ~threadManagerClass()
// Desc: threadManagerClass class destructor
//-----------------------------------------------------------------------------
threadManagerClass::~threadManagerClass()
{
// locals
unsigned int curThreadNo;
for (curThreadNo=0; curThreadNo<numThreads; curThreadNo++) {
CloseHandle(hBarrier[curThreadNo]);
}
DeleteCriticalSection(&csBarrier);
CloseHandle(hEventBarrierPassedByEveryBody);
if (hBarrier != NULL) delete [] hBarrier; hBarrier = NULL;
if (hThread != NULL) delete [] hThread; hThread = NULL;
if (threadId != NULL) delete [] threadId; threadId = NULL;
}
//-----------------------------------------------------------------------------
// Name: waitForOtherThreads()
// Desc:
//-----------------------------------------------------------------------------
void threadManagerClass::waitForOtherThreads(unsigned int threadNo)
{
// wait if other threads are still waiting at the barrier
//cout << endl << "thread=" << threadNo << ", numThreadsPassedBarrier= " << numThreadsPassedBarrier << ": " << "while (numThreadsPassedBarrier>0)";
if (numThreadsPassedBarrier>0) {
WaitForSingleObject(hEventBarrierPassedByEveryBody, INFINITE);
}
// a simple while (numThreadsPassedBarrier>0) {}; does not work, since the variable 'numThreadsPassedBarrier' is not updated, due to compiler optimizations
// set signal that barrier is reached
//cout << endl << "thread=" << threadNo << ", numThreadsPassedBarrier= " << numThreadsPassedBarrier << ": " << "SetEvent()";
SetEvent(hBarrier[threadNo]);
// enter the barrier one by one
//cout << endl << "thread=" << threadNo << ", numThreadsPassedBarrier= " << numThreadsPassedBarrier << ": " << "EnterCriticalSection()";
EnterCriticalSection(&csBarrier);
// if the first one which entered, then wait until other threads
if (numThreadsPassedBarrier==0) {
//cout << endl << "thread=" << threadNo << ", numThreadsPassedBarrier= " << numThreadsPassedBarrier << ": " << "WaitForMultipleObjects()";
WaitForMultipleObjects(numThreads, hBarrier, TRUE, INFINITE);
ResetEvent(hEventBarrierPassedByEveryBody);
}
// count threads which passed the barrier
//cout << endl << "thread=" << threadNo << ", numThreadsPassedBarrier= " << numThreadsPassedBarrier << ": " << "numThreadsPassedBarrier++";
numThreadsPassedBarrier++;
// the last one closes the door
//cout << endl << "thread=" << threadNo << ", numThreadsPassedBarrier= " << numThreadsPassedBarrier << ": " << "if (numThreadsPassedBarrier == numThreads) numThreadsPassedBarrier = 0";
if (numThreadsPassedBarrier == numThreads) {
numThreadsPassedBarrier = 0;
SetEvent(hEventBarrierPassedByEveryBody);
}
//cout << endl << "thread=" << threadNo << ", numThreadsPassedBarrier= " << numThreadsPassedBarrier << ": " << "LeaveCriticalSection()";
LeaveCriticalSection(&csBarrier);
}
//-----------------------------------------------------------------------------
// Name: getNumThreads()
// Desc:
//-----------------------------------------------------------------------------
unsigned int threadManagerClass::getNumThreads()
{
return numThreads;
}
//-----------------------------------------------------------------------------
// Name: setNumThreads()
// Desc:
//-----------------------------------------------------------------------------
bool threadManagerClass::setNumThreads(unsigned int newNumThreads)
{
// cancel if any thread running
EnterCriticalSection(&csBarrier);
for (unsigned int curThreadNo=0; curThreadNo<numThreads; curThreadNo++) {
if (hThread[curThreadNo]) return false;
}
for (unsigned int curThreadNo=0; curThreadNo<numThreads; curThreadNo++) {
CloseHandle(hBarrier[curThreadNo]);
}
numThreads = newNumThreads;
for (unsigned int curThreadNo=0; curThreadNo<numThreads; curThreadNo++) {
hBarrier[curThreadNo] = CreateEvent(NULL, false, false, NULL);
}
LeaveCriticalSection(&csBarrier);
return true;
}
//-----------------------------------------------------------------------------
// Name: pauseExecution()
// Desc:
//-----------------------------------------------------------------------------
void threadManagerClass::pauseExecution()
{
for (unsigned int curThread=0; curThread<numThreads; curThread++) {
// unsuspend all threads
if (!executionPaused) {
SuspendThread(hThread[curThread]);
// suspend all threads
} else {
ResumeThread(hThread[curThread]);
}
}
executionPaused = (!executionPaused);
}
//-----------------------------------------------------------------------------
// Name: cancelExecution()
// Desc: Stops executeParallelLoop() before the next iteration.
// When executeInParallel() was called, user has to handle cancellation by himself.
//-----------------------------------------------------------------------------
void threadManagerClass::cancelExecution()
{
termineAllThreads = true;
executionCancelled = true;
if (executionPaused) {
pauseExecution();
}
}
//-----------------------------------------------------------------------------
// Name: uncancelExecution()
// Desc:
//-----------------------------------------------------------------------------
void threadManagerClass::uncancelExecution()
{
executionCancelled = false;
}
//-----------------------------------------------------------------------------
// Name: wasExecutionCancelled()
// Desc:
//-----------------------------------------------------------------------------
bool threadManagerClass::wasExecutionCancelled()
{
return executionCancelled;
}
//-----------------------------------------------------------------------------
// Name: getThreadId()
// Desc: Returns a number from 0 to 'numThreads'-1. Returns 0 if the function fails.
//-----------------------------------------------------------------------------
unsigned int threadManagerClass::getThreadNumber()
{
// locals
DWORD curThreadId = GetCurrentThreadId();
unsigned int curThreadNo;
for (curThreadNo=0; curThreadNo<numThreads; curThreadNo++) {
if (curThreadId == threadId[curThreadNo]) {
return curThreadNo;
}
}
return 0;
}
//-----------------------------------------------------------------------------
// Name: executeInParallel()
// Desc: lpParameter is an array of size numThreads.
//-----------------------------------------------------------------------------
unsigned int threadManagerClass::executeInParallel(DWORD threadProc(void* pParameter), void *pParameter, unsigned int parameterStructSize)
{
// locals
unsigned int curThreadNo;
SIZE_T dwStackSize = 0;
// parameters ok?
if (pParameter == NULL) return TM_RETURN_VALUE_INVALID_PARAM;
// globals
termineAllThreads = false;
// create threads
for (curThreadNo=0; curThreadNo<numThreads; curThreadNo++) {
hThread[curThreadNo] = CreateThread(NULL, dwStackSize, (LPTHREAD_START_ROUTINE) threadProc, (void*) (((char *) pParameter) + curThreadNo * parameterStructSize), CREATE_SUSPENDED, &threadId[curThreadNo]);
SetThreadPriority(hThread[curThreadNo], THREAD_PRIORITY_BELOW_NORMAL);
if (hThread[curThreadNo] == NULL) {
for (curThreadNo; curThreadNo>0; curThreadNo--) {
CloseHandle(hThread[curThreadNo-1]);
hThread[curThreadNo-1] = NULL;
}
return TM_RETURN_VALUE_UNEXPECTED_ERROR;
}
}
// start threads
for (curThreadNo=0; curThreadNo<numThreads; curThreadNo++) {
if (!executionPaused) ResumeThread(hThread[curThreadNo]);
}
// wait for every thread to end
WaitForMultipleObjects(numThreads, hThread, TRUE, INFINITE);
// Close all thread handles upon completion.
for (curThreadNo=0; curThreadNo<numThreads; curThreadNo++) {
CloseHandle(hThread[curThreadNo]);
hThread[curThreadNo] = NULL;
threadId[curThreadNo] = 0;
}
// everything ok
if (executionCancelled) {
return TM_RETURN_VALUE_EXECUTION_CANCELLED;
} else {
return TM_RETURN_VALUE_OK;
}
}
//-----------------------------------------------------------------------------
// Name: executeInParallel()
// Desc:
// lpParameter - an array of size numThreads
// finalValue - this value is part of the iteration, meaning that index ranges from initialValue to finalValue including both border values
//-----------------------------------------------------------------------------
unsigned int threadManagerClass::executeParallelLoop( DWORD threadProc(void* pParameter, int index),
void * pParameter,
unsigned int parameterStructSize,
unsigned int scheduleType,
int initialValue,
int finalValue,
int inkrement)
{
// parameters ok?
if (executionCancelled == true) return TM_RETURN_VALUE_EXECUTION_CANCELLED;
if (pParameter == NULL) return TM_RETURN_VALUE_INVALID_PARAM;
if (scheduleType >= TM_SCHEDULE_NUM_TYPES) return TM_RETURN_VALUE_INVALID_PARAM;
if (inkrement == 0) return TM_RETURN_VALUE_INVALID_PARAM;
if (abs(finalValue-initialValue)==abs(inkrement)) return TM_RETURN_VALUE_INVALID_PARAM;
// locals
unsigned int curThreadNo; // the threads are enumerated from 0 to numThreads-1
int numIterations = (finalValue - initialValue) / inkrement + 1; // total number of iterations
int chunkSize = 0; // number of iterations per chunk
SIZE_T dwStackSize = 0; // initital stack size of each thread. 0 means default size ~1MB
forLoopStruct * forLoopParameters = new forLoopStruct[numThreads]; //
// globals
termineAllThreads = false;
// create threads
for (curThreadNo=0; curThreadNo<numThreads; curThreadNo++) {
forLoopParameters[curThreadNo].pParameter = (pParameter!=NULL ? (void*) (((char *) pParameter) + curThreadNo * parameterStructSize) : NULL);
forLoopParameters[curThreadNo].threadManager = this;
forLoopParameters[curThreadNo].threadProc = threadProc;
forLoopParameters[curThreadNo].inkrement = inkrement;
forLoopParameters[curThreadNo].scheduleType = scheduleType;
switch (scheduleType)
{
case TM_SCHEDULE_STATIC:
chunkSize = numIterations / numThreads + (curThreadNo<numIterations%numThreads ? 1 : 0);
if (curThreadNo==0) {
forLoopParameters[curThreadNo].initialValue = initialValue;
} else {
forLoopParameters[curThreadNo].initialValue = forLoopParameters[curThreadNo-1].finalValue + 1;
}
forLoopParameters[curThreadNo].finalValue = forLoopParameters[curThreadNo].initialValue + chunkSize - 1;
break;
case TM_SCHEDULE_DYNAMIC:
return TM_RETURN_VALUE_INVALID_PARAM;
break;
case TM_SCHEDULE_GUIDED:
return TM_RETURN_VALUE_INVALID_PARAM;
break;
case TM_SCHEDULE_RUNTIME:
return TM_RETURN_VALUE_INVALID_PARAM;
break;
}
// create suspended thread
hThread[curThreadNo] = CreateThread(NULL, dwStackSize, threadForLoop, (LPVOID) (&forLoopParameters[curThreadNo]), CREATE_SUSPENDED, &threadId[curThreadNo]);
SetThreadPriority(hThread[curThreadNo], THREAD_PRIORITY_BELOW_NORMAL);
if (hThread[curThreadNo] == NULL) {
for (curThreadNo; curThreadNo>0; curThreadNo--) {
CloseHandle(hThread[curThreadNo-1]);
hThread[curThreadNo-1] = NULL;
}
return TM_RETURN_VALUE_UNEXPECTED_ERROR;
}
//DWORD dwThreadAffinityMask = 1 << curThreadNo;
//SetThreadAffinityMask(hThread[curThreadNo], &dwThreadAffinityMask);
}
// start threads, but don't resume if in pause mode
for (curThreadNo=0; curThreadNo<numThreads; curThreadNo++) {
if (!executionPaused) ResumeThread(hThread[curThreadNo]);
}
// wait for every thread to end
WaitForMultipleObjects(numThreads, hThread, TRUE, INFINITE);
// Close all thread handles upon completion.
for (curThreadNo=0; curThreadNo<numThreads; curThreadNo++) {
CloseHandle(hThread[curThreadNo]);
hThread[curThreadNo] = NULL;
threadId[curThreadNo] = 0;
}
delete [] forLoopParameters;
// everything ok
if (executionCancelled) {
return TM_RETURN_VALUE_EXECUTION_CANCELLED;
} else {
return TM_RETURN_VALUE_OK;
}
}
//-----------------------------------------------------------------------------
// Name: threadForLoop()
// Desc:
//-----------------------------------------------------------------------------
DWORD WINAPI threadManagerClass::threadForLoop(LPVOID lpParameter)
{
// locals
forLoopStruct * forLoopParameters = (forLoopStruct *) lpParameter;
int index;
switch (forLoopParameters->scheduleType)
{
case TM_SCHEDULE_STATIC:
for (index=forLoopParameters->initialValue; (forLoopParameters->inkrement<0) ? index >= forLoopParameters->finalValue : index <= forLoopParameters->finalValue; index += forLoopParameters->inkrement) {
switch (forLoopParameters->threadProc(forLoopParameters->pParameter, index))
{
case TM_RETURN_VALUE_OK:
break;
case TM_RETURN_VALUE_TERMINATE_ALL_THREADS:
forLoopParameters->threadManager->termineAllThreads = true;
break;
default:
break;
}
if (forLoopParameters->threadManager->termineAllThreads) break;
}
break;
case TM_SCHEDULE_DYNAMIC:
return TM_RETURN_VALUE_INVALID_PARAM;
break;
case TM_SCHEDULE_GUIDED:
return TM_RETURN_VALUE_INVALID_PARAM;
break;
case TM_SCHEDULE_RUNTIME:
return TM_RETURN_VALUE_INVALID_PARAM;
break;
}
return TM_RETURN_VALUE_OK;
}
/*** To Do's ********************************************************************************
- Beschränkung auf 'int' kann zu Überlauf führen, wenn mehr states in einer layer vorliegen.
==> Vielleicht mit class templates arbeiten
*********************************************************************************************/

147
src/perfect/threadManager.h Normal file
View File

@ -0,0 +1,147 @@
/*********************************************************************\
threadManager.h
Copyright (c) Thomas Weber. All rights reserved.
Licensed under the MIT License.
https://github.com/madweasel/madweasels-cpp
\*********************************************************************/
#ifndef THREADMANAGER_H
#define THREADMANAGER_H
// standard library & win32 api
#include <windows.h>
#include <cstdio>
#include <iostream>
using namespace std; // use standard library namespace
/*** Konstanten ******************************************************/
#define TM_SCHEDULE_USER_DEFINED 0
#define TM_SCHEDULE_STATIC 1
#define TM_SCHEDULE_DYNAMIC 2
#define TM_SCHEDULE_GUIDED 3
#define TM_SCHEDULE_RUNTIME 4
#define TM_SCHEDULE_NUM_TYPES 5
#define TM_RETURN_VALUE_OK 0
#define TM_RETURN_VALUE_TERMINATE_ALL_THREADS 1
#define TM_RETURN_VALUE_EXECUTION_CANCELLED 2
#define TM_RETURN_VALUE_INVALID_PARAM 3
#define TM_RETURN_VALUE_UNEXPECTED_ERROR 4
/*** Makros ******************************************************/
/*** Strukturen ******************************************************/
/*** Klassen *********************************************************/
class threadManagerClass
{
private:
// structures
struct forLoopStruct
{
unsigned int scheduleType;
int inkrement;
int initialValue;
int finalValue;
void * pParameter;
DWORD (*threadProc)(void* pParameter, int index); // pointer to the user function to be executed by the threads
threadManagerClass *threadManager;
};
// Variables
unsigned int numThreads; // number of threads
HANDLE * hThread; // array of size 'numThreads' containing the thread handles
DWORD * threadId; // array of size 'numThreads' containing the thread ids
bool termineAllThreads;
bool executionPaused; // switch for the
bool executionCancelled; // true when cancelExecution() was called
// barier stuff
HANDLE hEventBarrierPassedByEveryBody;
HANDLE * hBarrier; // array of size 'numThreads' containing the event handles for the barrier
unsigned int numThreadsPassedBarrier;
CRITICAL_SECTION csBarrier;
// functions
static DWORD WINAPI threadForLoop (LPVOID lpParameter);
public:
class threadVarsArrayItem
{
public:
unsigned int curThreadNo;
virtual void initializeElement () {};
virtual void destroyElement () {};
virtual void reduce () {};
};
template <class varType> class threadVarsArray
{
public:
unsigned int numberOfThreads;
varType * item;
threadVarsArray(unsigned int numberOfThreads, varType& master)
{
this->numberOfThreads = numberOfThreads;
this->item = new varType[numberOfThreads];
for (unsigned int threadCounter=0; threadCounter<numberOfThreads; threadCounter++) {
item[threadCounter].curThreadNo = threadCounter;
item[threadCounter].initializeElement(master);
item[threadCounter].curThreadNo = threadCounter; // if 'curThreadNo' is overwritten in 'initializeElement()'
}
};
~threadVarsArray()
{
for (unsigned int threadCounter=0; threadCounter<numberOfThreads; threadCounter++) {
item[threadCounter].destroyElement();
}
delete [] item;
};
void * getPointerToArray()
{
return (void*) item;
};
unsigned int getSizeOfArray()
{
return sizeof(varType);
};
void reduce()
{
for (unsigned int threadCounter=0; threadCounter<numberOfThreads; threadCounter++) {
item[threadCounter].reduce();
}
};
};
// Constructor / destructor
threadManagerClass();
~threadManagerClass();
// Functions
unsigned int getThreadNumber ();
unsigned int getNumThreads ();
bool setNumThreads (unsigned int newNumThreads);
void waitForOtherThreads (unsigned int threadNo);
void pauseExecution (); // un-/suspend all threads
void cancelExecution (); // termineAllThreads auf true
bool wasExecutionCancelled ();
void uncancelExecution (); // sets executionCancelled to false, otherwise executeParellelLoop returns immediatelly
//... void setCallBackFunction (void userFunction(void* pUser), void* pUser, DWORD milliseconds); // a user function which is called every x-milliseconds during execution between two iterations
// execute
unsigned int executeInParallel (DWORD threadProc(void* pParameter ), void *pParameter, unsigned int parameterStructSize);
unsigned int executeParallelLoop (DWORD threadProc(void* pParameter, int index), void *pParameter, unsigned int parameterStructSize, unsigned int scheduleType, int initialValue, int finalValue, int inkrement);
};
#endif