From b938e0fd667b399e9b11c799e29d2cc46a82345f Mon Sep 17 00:00:00 2001 From: Calcitem Date: Thu, 21 Jan 2021 00:15:08 +0800 Subject: [PATCH] prefect: Rename position to mill and add blank lines --- src/perfect/bufferedFile.cpp | 12 + src/perfect/cyclicArray.cpp | 5 +- src/perfect/main.cpp | 13 +- src/perfect/{position.cpp => mill.cpp} | 96 ++++--- src/perfect/{position.h => mill.h} | 27 +- src/perfect/millAI.cpp | 1 + src/perfect/millAI.h | 1 + src/perfect/minMaxAI.cpp | 10 +- src/perfect/miniMax.cpp | 6 +- src/perfect/miniMax.h | 31 +++ src/perfect/miniMaxAI.h | 14 + src/perfect/miniMaxWin.h | 2 + src/perfect/miniMax_alphaBetaAlgorithmn.cpp | 13 +- src/perfect/miniMax_database.cpp | 11 +- src/perfect/miniMax_retroAnalysis.cpp | 23 +- src/perfect/miniMax_statistics.cpp | 7 +- src/perfect/miniMax_test.cpp | 14 +- src/perfect/perfect.vcxproj | 4 +- src/perfect/perfect.vcxproj.filters | 6 +- src/perfect/perfectAI.cpp | 280 +++++++++++--------- src/perfect/perfectAI.h | 1 + src/perfect/randomAI.cpp | 3 - src/perfect/strLib.cpp | 9 +- src/perfect/threadManager.cpp | 21 +- src/perfect/threadManager.h | 2 + 25 files changed, 384 insertions(+), 228 deletions(-) rename src/perfect/{position.cpp => mill.cpp} (92%) rename src/perfect/{position.h => mill.h} (95%) diff --git a/src/perfect/bufferedFile.cpp b/src/perfect/bufferedFile.cpp index b76e3b1c..7664e1cb 100644 --- a/src/perfect/bufferedFile.cpp +++ b/src/perfect/bufferedFile.cpp @@ -33,6 +33,7 @@ BufferedFile::BufferedFile(unsigned int numberOfThreads, unsigned int bufferSize bytesInReadBuffer[curThread] = 0; bytesInWriteBuffer[curThread] = 0; } + InitializeCriticalSection(&csIO); // Open Database-File (FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH | FILE_FLAG_RANDOM_ACCESS) @@ -80,6 +81,7 @@ long long BufferedFile::getFileSize() LARGE_INTEGER liFileSize; GetFileSizeEx(hFile, &liFileSize); fileSize = liFileSize.QuadPart; + return fileSize; } @@ -93,6 +95,7 @@ bool BufferedFile::flushBuffers() writeDataToFile(hFile, curWritingPointer[threadNo] - bytesInWriteBuffer[threadNo], bytesInWriteBuffer[threadNo], &writeBuffer[threadNo * bufferSize + 0]); bytesInWriteBuffer[threadNo] = 0; } + return true; } @@ -109,9 +112,11 @@ void BufferedFile::writeDataToFile(HANDLE hFile, long long offset, unsigned int liDistanceToMove.QuadPart = offset; EnterCriticalSection(&csIO); + while (!SetFilePointerEx(hFile, liDistanceToMove, nullptr, FILE_BEGIN)) cout << endl << "SetFilePointerEx failed!"; + while (restingBytes > 0) { if (WriteFile(hFile, pData, sizeInBytes, &dwBytesWritten, nullptr) == TRUE) { restingBytes -= dwBytesWritten; @@ -124,6 +129,7 @@ void BufferedFile::writeDataToFile(HANDLE hFile, long long offset, unsigned int << "WriteFile Failed!"; } } + LeaveCriticalSection(&csIO); } @@ -140,9 +146,11 @@ void BufferedFile::readDataFromFile(HANDLE hFile, long long offset, unsigned int liDistanceToMove.QuadPart = offset; EnterCriticalSection(&csIO); + while (!SetFilePointerEx(hFile, liDistanceToMove, nullptr, FILE_BEGIN)) cout << endl << "SetFilePointerEx failed!"; + while (restingBytes > 0) { if (ReadFile(hFile, pData, sizeInBytes, &dwBytesRead, nullptr) == TRUE) { restingBytes -= dwBytesRead; @@ -155,6 +163,7 @@ void BufferedFile::readDataFromFile(HANDLE hFile, long long offset, unsigned int << "ReadFile Failed!"; } } + LeaveCriticalSection(&csIO); } @@ -176,6 +185,7 @@ bool BufferedFile::writeBytes(unsigned int threadNo, long long positionInFile, u // parameters ok? if (threadNo >= numThreads) return false; + if (pData == nullptr) return false; @@ -215,6 +225,7 @@ bool BufferedFile::readBytes(unsigned int threadNo, long long positionInFile, un // parameters ok? if (threadNo >= numThreads) return false; + if (pData == nullptr) return false; @@ -225,6 +236,7 @@ bool BufferedFile::readBytes(unsigned int threadNo, long long positionInFile, un 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; diff --git a/src/perfect/cyclicArray.cpp b/src/perfect/cyclicArray.cpp index 9eae6953..f8ff2934 100644 --- a/src/perfect/cyclicArray.cpp +++ b/src/perfect/cyclicArray.cpp @@ -148,6 +148,7 @@ bool CyclicArray::addBytes(unsigned int numBytes, unsigned char *pData) // set pointer to beginnig of writing block curWritingPointer = writingBlock; curWritingBlock = (curWritingBlock + 1) % numBlocks; + if (curWritingBlock == 0) readWriteInSameRound = false; } @@ -194,7 +195,6 @@ bool CyclicArray::takeBytes(unsigned int numBytes, unsigned char *pData) // load next block? if (curReadingPointer == readingBlock + blockSize) { - // go to next block curReadingBlock = (curReadingBlock + 1) % numBlocks; if (curReadingBlock == 0) @@ -204,7 +204,6 @@ bool CyclicArray::takeBytes(unsigned int numBytes, unsigned char *pData) if (curReadingBlock == curWritingBlock) { curReadingPointer = writingBlock; } else { - // set pointer to beginnig of reading block curReadingPointer = readingBlock; @@ -269,7 +268,6 @@ bool CyclicArray::loadFile(const char *fileName, LONGLONG &numBytesLoaded) // for (curBlock = 0; curBlock < numBlocksInFile - 1; curBlock++, curOffset += blockSize) { - // load data from file readDataFromFile(hLoadFile, curOffset, blockSize, dataInFile); @@ -323,7 +321,6 @@ bool CyclicArray::saveFile(const char *fileName) dataInFile = new unsigned char[blockSize]; do { - // copy current block if (curBlock == curWritingBlock && curBlock == curReadingBlock) { pointer = curReadingPointer; diff --git a/src/perfect/main.cpp b/src/perfect/main.cpp index 7dc116f0..cb73f45a 100644 --- a/src/perfect/main.cpp +++ b/src/perfect/main.cpp @@ -1,7 +1,7 @@ #include #include #include -#include "position.h" +#include "mill.h" #include "miniMaxAI.h" #include "randomAI.h" #include "perfectAI.h" @@ -26,7 +26,7 @@ int main(void) bool playerTwoHuman = false; char ch[100]; unsigned int pushFrom, pushTo; - Position *pos = new Position(); + Mill *pos = new Mill(); PerfectAI *ai = new PerfectAI(databaseDirectory); SetPriorityClass(GetCurrentProcess(), BELOW_NORMAL_PRIORITY_CLASS); @@ -48,17 +48,20 @@ int main(void) #endif // SELF_PLAY if (calculateDatabase) { - // calculate ai->calculateDatabase(MAX_DEPTH_OF_TREE, false); // test database cout << endl << "Begin test starting from layer: "; + startTestFromLayer; + cout << endl << "End test at layer: "; + endTestAtLayer; + ai->testLayers(startTestFromLayer, endTestAtLayer); } else { @@ -134,12 +137,12 @@ int main(void) break; } - } while (pos->do_move(pushFrom, pushTo) == false); + } while (pos->doMove(pushFrom, pushTo) == false); // Computer } else { cout << "\n"; - pos->do_move(pushFrom, pushTo); + pos->doMove(pushFrom, pushTo); } } while (pos->getWinner() == 0); diff --git a/src/perfect/position.cpp b/src/perfect/mill.cpp similarity index 92% rename from src/perfect/position.cpp rename to src/perfect/mill.cpp index 0aaa86eb..3754005e 100644 --- a/src/perfect/position.cpp +++ b/src/perfect/mill.cpp @@ -1,18 +1,18 @@ /********************************************************************* - Position.cpp + Mill.cpp Copyright (c) Thomas Weber. All rights reserved. Copyright (C) 2021 The Sanmill developers (see AUTHORS file) Licensed under the MIT License. https://github.com/madweasel/madweasels-cpp \*********************************************************************/ -#include "position.h" +#include "mill.h" //----------------------------------------------------------------------------- -// Name: Position() -// Desc: Position class constructor +// Name: Mill() +// Desc: Mill class constructor //----------------------------------------------------------------------------- -Position::Position() +Mill::Mill() { srand((unsigned)time(nullptr)); @@ -27,19 +27,19 @@ Position::Position() } //----------------------------------------------------------------------------- -// Name: ~Position() -// Desc: Position class destructor +// Name: ~Mill() +// Desc: Mill class destructor //----------------------------------------------------------------------------- -Position::~Position() +Mill::~Mill() { exit(); } //----------------------------------------------------------------------------- // Name: deleteArrays() -// Desc: Deletes all arrays the Position class has created. +// Desc: Deletes all arrays the Mill class has created. //----------------------------------------------------------------------------- -void Position::exit() +void Mill::exit() { SAFE_DELETE_ARRAY(moveLogFrom); SAFE_DELETE_ARRAY(moveLogTo); @@ -50,9 +50,9 @@ void Position::exit() //----------------------------------------------------------------------------- // Name: beginNewGame() -// Desc: Reinitializes the Position object. +// Desc: Reinitializes the Mill object. //----------------------------------------------------------------------------- -void Position::beginNewGame(MillAI *firstPlayerAI, MillAI *secondPlayerAI, int currentPlayer) +void Mill::beginNewGame(MillAI *firstPlayerAI, MillAI *secondPlayerAI, int currentPlayer) { // free mem exit(); @@ -67,6 +67,7 @@ void Position::beginNewGame(MillAI *firstPlayerAI, MillAI *secondPlayerAI, int c } else { beginningPlayer = (rand() % 2) ? field.playerOne : field.playerTwo; } + field.curPlayer->id = beginningPlayer; field.oppPlayer->id = (field.curPlayer->id == field.playerTwo) ? field.playerOne : field.playerTwo; @@ -85,7 +86,7 @@ void Position::beginNewGame(MillAI *firstPlayerAI, MillAI *secondPlayerAI, int c // Name: startSettingPhase() // Desc: //----------------------------------------------------------------------------- -bool Position::startSettingPhase(MillAI *firstPlayerAI, MillAI *secondPlayerAI, int currentPlayer, bool settingPhase) +bool Mill::startSettingPhase(MillAI *firstPlayerAI, MillAI *secondPlayerAI, int currentPlayer, bool settingPhase) { beginNewGame(firstPlayerAI, secondPlayerAI, currentPlayer); @@ -98,7 +99,7 @@ bool Position::startSettingPhase(MillAI *firstPlayerAI, MillAI *secondPlayerAI, // Name: setUpCalcPossibleMoves() // Desc: Calculates and set the number of possible moves for the passed player considering the game state stored in the 'board' variable. //----------------------------------------------------------------------------- -void Position::setUpCalcPossibleMoves(Player *player) +void Mill::setUpCalcPossibleMoves(Player *player) { // locals unsigned int i, j, k, movingDirection; @@ -137,7 +138,7 @@ void Position::setUpCalcPossibleMoves(Player *player) // Name: setUpSetWarningAndMill() // Desc: //----------------------------------------------------------------------------- -void Position::setUpSetWarningAndMill(unsigned int stone, unsigned int firstNeighbour, unsigned int secondNeighbour) +void Mill::setUpSetWarningAndMill(unsigned int stone, unsigned int firstNeighbour, unsigned int secondNeighbour) { // locals int rowOwner = field.board[stone]; @@ -152,10 +153,10 @@ void Position::setUpSetWarningAndMill(unsigned int stone, unsigned int firstNeig } //----------------------------------------------------------------------------- -// Name: put_piece() +// Name: putPiece() // Desc: Put a stone onto the board during the setting phase. //----------------------------------------------------------------------------- -bool Position::put_piece(unsigned int pos, int player) +bool Mill::putPiece(unsigned int pos, int player) { // locals unsigned int i; @@ -237,7 +238,7 @@ bool Position::put_piece(unsigned int pos, int player) // Name: settingPhaseHasFinished() // Desc: This function has to be called when the setting phase has finished. //----------------------------------------------------------------------------- -bool Position::settingPhaseHasFinished() +bool Mill::settingPhaseHasFinished() { // remember initialField field.copyBoard(&initialField); @@ -249,7 +250,7 @@ bool Position::settingPhaseHasFinished() // Name: getField() // Desc: Copy the current board state into the array 'pField'. //----------------------------------------------------------------------------- -bool Position::getField(int *pField) +bool Mill::getField(int *pField) { unsigned int index; @@ -271,7 +272,7 @@ bool Position::getField(int *pField) // Name: getLog() // Desc: Copy the whole history of moves into the passed arrays, which must be of size [MAX_NUM_MOVES]. //----------------------------------------------------------------------------- -void Position::getLog(unsigned int &numMovesDone, unsigned int *from, unsigned int *to) +void Mill::getLog(unsigned int &numMovesDone, unsigned int *from, unsigned int *to) { unsigned int index; @@ -287,7 +288,7 @@ void Position::getLog(unsigned int &numMovesDone, unsigned int *from, unsigned i // Name: setNextPlayer() // Desc: Current player and opponent player are switched in the board struct. //----------------------------------------------------------------------------- -void Position::setNextPlayer() +void Mill::setNextPlayer() { Player *tmpPlayer; @@ -300,7 +301,7 @@ void Position::setNextPlayer() // Name: isCurrentPlayerHuman() // Desc: Returns true if the current player is not assigned to an AI. //----------------------------------------------------------------------------- -bool Position::isCurrentPlayerHuman() +bool Mill::isCurrentPlayerHuman() { if (field.curPlayer->id == field.playerOne) return (playerOneAI == nullptr) ? true : false; @@ -312,7 +313,7 @@ bool Position::isCurrentPlayerHuman() // Name: isOpponentPlayerHuman() // Desc: Returns true if the opponent player is not assigned to an AI. //----------------------------------------------------------------------------- -bool Position::isOpponentPlayerHuman() +bool Mill::isOpponentPlayerHuman() { if (field.oppPlayer->id == field.playerOne) return (playerOneAI == nullptr) ? true : false; @@ -324,7 +325,7 @@ bool Position::isOpponentPlayerHuman() // Name: setAI() // Desc: Assigns an AI to a player. //----------------------------------------------------------------------------- -void Position::setAI(int player, MillAI *AI) +void Mill::setAI(int player, MillAI *AI) { if (player == field.playerOne) { playerOneAI = AI; @@ -338,7 +339,7 @@ void Position::setAI(int player, MillAI *AI) // Name: getChoiceOfSpecialAI() // Desc: Returns the move the passed AI would do. //----------------------------------------------------------------------------- -void Position::getChoiceOfSpecialAI(MillAI *AI, unsigned int *pushFrom, unsigned int *pushTo) +void Mill::getChoiceOfSpecialAI(MillAI *AI, unsigned int *pushFrom, unsigned int *pushTo) { fieldStruct theField; *pushFrom = field.size; @@ -354,7 +355,7 @@ void Position::getChoiceOfSpecialAI(MillAI *AI, unsigned int *pushFrom, unsigned // Name: getComputersChoice() // Desc: Returns the move the AI of the current player would do. //----------------------------------------------------------------------------- -void Position::getComputersChoice(unsigned int *pushFrom, unsigned int *pushTo) +void Mill::getComputersChoice(unsigned int *pushFrom, unsigned int *pushTo) { fieldStruct theField; *pushFrom = field.size; @@ -379,7 +380,7 @@ void Position::getComputersChoice(unsigned int *pushFrom, unsigned int *pushTo) // Name: isNormalMovePossible() // Desc: 'Normal' in this context means, by moving the stone along a connection without jumping. //----------------------------------------------------------------------------- -bool Position::isNormalMovePossible(unsigned int from, unsigned int to, Player *player) +bool Mill::isNormalMovePossible(unsigned int from, unsigned int to, Player *player) { // locals unsigned int movingDirection, i; @@ -419,7 +420,7 @@ bool Position::isNormalMovePossible(unsigned int from, unsigned int to, Player * // Name: calcPossibleMoves() // Desc: ... //----------------------------------------------------------------------------- -void Position::calcPossibleMoves(Player *player) +void Mill::calcPossibleMoves(Player *player) { // locals unsigned int i, j; @@ -456,7 +457,7 @@ void Position::calcPossibleMoves(Player *player) // Name: setWarningAndMill() // Desc: //----------------------------------------------------------------------------- -void Position::setWarningAndMill(unsigned int stone, unsigned int firstNeighbour, unsigned int secondNeighbour, bool isNewStone) +void Mill::setWarningAndMill(unsigned int stone, unsigned int firstNeighbour, unsigned int secondNeighbour, bool isNewStone) { // locals int rowOwner = field.board[stone]; @@ -483,7 +484,7 @@ void Position::setWarningAndMill(unsigned int stone, unsigned int firstNeighbour // Name: updateMillsAndWarnings() // Desc: //----------------------------------------------------------------------------- -void Position::updateMillsAndWarnings(unsigned int newStone) +void Mill::updateMillsAndWarnings(unsigned int newStone) { // locals unsigned int i; @@ -492,8 +493,10 @@ void Position::updateMillsAndWarnings(unsigned int newStone) // 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 @@ -516,10 +519,10 @@ void Position::updateMillsAndWarnings(unsigned int newStone) } //----------------------------------------------------------------------------- -// Name: do_move() +// Name: doMove() // Desc: //----------------------------------------------------------------------------- -bool Position::do_move(unsigned int pushFrom, unsigned int pushTo) +bool Mill::doMove(unsigned int pushFrom, unsigned int pushTo) { // avoid index override if (movesDone >= MAX_NUM_MOVES) @@ -654,7 +657,7 @@ bool Position::do_move(unsigned int pushFrom, unsigned int pushTo) // Name: setCurrentGameState() // Desc: Set an arbitrary game state as the current one. //----------------------------------------------------------------------------- -bool Position::setCurrentGameState(fieldStruct *curState) +bool Mill::setCurrentGameState(fieldStruct *curState) { curState->copyBoard(&field); @@ -663,8 +666,10 @@ bool Position::setCurrentGameState(fieldStruct *curState) 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; @@ -675,7 +680,7 @@ bool Position::setCurrentGameState(fieldStruct *curState) // Name: compareWithField() // Desc: Compares the current 'board' variable with the passed one. 'stoneMoveAble[]' is ignored. //----------------------------------------------------------------------------- -bool Position::compareWithField(fieldStruct *compareField) +bool Mill::compareWithField(fieldStruct *compareField) { unsigned int i, j; bool ret = true; @@ -684,6 +689,7 @@ bool Position::compareWithField(fieldStruct *compareField) cout << "error - curPlayer differs!" << endl; ret = false; } + if (!comparePlayers(field.oppPlayer, compareField->oppPlayer)) { cout << "error - oppPlayer differs!" << endl; ret = false; @@ -693,37 +699,41 @@ bool Position::compareWithField(fieldStruct *compareField) 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.board[i] != compareField->board[i]) { cout << "error - board[] 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 (board.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; @@ -738,7 +748,7 @@ bool Position::compareWithField(fieldStruct *compareField) // Name: comparePlayers() // Desc: Compares the two passed players and returns false if they differ. //----------------------------------------------------------------------------- -bool Position::comparePlayers(Player *playerA, Player *playerB) +bool Mill::comparePlayers(Player *playerA, Player *playerB) { // unsigned int i; bool ret = true; @@ -747,18 +757,22 @@ bool Position::comparePlayers(Player *playerA, Player *playerB) 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; @@ -775,7 +789,7 @@ bool Position::comparePlayers(Player *playerA, Player *playerB) // Desc: Calls the printBoard() function of the current board. // Prints the current game state on the screen. //----------------------------------------------------------------------------- -void Position::printBoard() +void Mill::printBoard() { field.printBoard(); } @@ -784,7 +798,7 @@ void Position::printBoard() // Name: undo_move() // Desc: Sets the initial board as the current one and apply all (minus one) moves from the move history. //----------------------------------------------------------------------------- -void Position::undo_move(void) +void Mill::undo_move(void) { // locals unsigned int *moveLogFrom_bak = new unsigned int[movesDone]; @@ -808,7 +822,7 @@ void Position::undo_move(void) // and play again for (i = 0; i < movesDone_bak - 1; i++) { - do_move(moveLogFrom_bak[i], moveLogTo_bak[i]); + doMove(moveLogFrom_bak[i], moveLogTo_bak[i]); } } @@ -821,7 +835,7 @@ void Position::undo_move(void) // Name: calcNumberOfRestingStones() // Desc: //----------------------------------------------------------------------------- -void Position::calcNumberOfRestingStones(int &numWhiteStonesResting, int &numBlackStonesResting) +void Mill::calcNumberOfRestingStones(int &numWhiteStonesResting, int &numBlackStonesResting) { if (getCurrentPlayer() == fieldStruct::playerTwo) { numWhiteStonesResting = fieldStruct::numStonesPerPlayer - field.curPlayer->numStonesMissing - field.curPlayer->numStones; diff --git a/src/perfect/position.h b/src/perfect/mill.h similarity index 95% rename from src/perfect/position.h rename to src/perfect/mill.h index 3e52f96c..e69843fc 100644 --- a/src/perfect/position.h +++ b/src/perfect/mill.h @@ -1,13 +1,13 @@ /*********************************************************************\ - Position.h + Mill.h Copyright (c) Thomas Weber. All rights reserved. Copyright (C) 2021 The Sanmill developers (see AUTHORS file) Licensed under the MIT License. https://github.com/madweasel/madweasels-cpp \*********************************************************************/ -#ifndef MUEHLE_H -#define MUEHLE_H +#ifndef MILL_H +#define MILL_H #include #include @@ -42,7 +42,7 @@ using namespace std; /*** Klassen *********************************************************/ -class Position +class Mill { private: // Variables @@ -64,21 +64,21 @@ private: public: // Constructor / destructor - Position(); - ~Position(); + Mill(); + ~Mill(); // Functions void undo_move(); void beginNewGame(MillAI *firstPlayerAI, MillAI *secondPlayerAI, int currentPlayer); void setAI(int player, MillAI *AI); - bool do_move(unsigned int pushFrom, unsigned int pushTo); + bool doMove(unsigned int pushFrom, unsigned int pushTo); void getComputersChoice(unsigned int *pushFrom, unsigned int *pushTo); bool setCurrentGameState(fieldStruct *curState); bool compareWithField(fieldStruct *compareField); bool comparePlayers(Player *playerA, Player *playerB); void printBoard(); bool startSettingPhase(MillAI *firstPlayerAI, MillAI *secondPlayerAI, int currentPlayer, bool settingPhase); - bool put_piece(unsigned int pos, int player); + bool putPiece(unsigned int pos, int player); bool settingPhaseHasFinished(); void getChoiceOfSpecialAI(MillAI *AI, unsigned int *pushFrom, unsigned int *pushTo); void setUpCalcPossibleMoves(Player *player); @@ -90,46 +90,57 @@ public: 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; diff --git a/src/perfect/millAI.cpp b/src/perfect/millAI.cpp index d8145ea6..04134eee 100644 --- a/src/perfect/millAI.cpp +++ b/src/perfect/millAI.cpp @@ -123,6 +123,7 @@ void Player::copyPlayer(Player *destination) 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]; } diff --git a/src/perfect/millAI.h b/src/perfect/millAI.h index e0e9a51a..125a9ba8 100644 --- a/src/perfect/millAI.h +++ b/src/perfect/millAI.h @@ -94,6 +94,7 @@ public: { dummyField.createBoard(); }; + ~MillAI() { dummyField.deleteBoard(); diff --git a/src/perfect/minMaxAI.cpp b/src/perfect/minMaxAI.cpp index 9baf06ae..dc56d371 100644 --- a/src/perfect/minMaxAI.cpp +++ b/src/perfect/minMaxAI.cpp @@ -116,7 +116,6 @@ unsigned int *MiniMaxAI::getPossSettingPhase(unsigned int *numPossibilities, voi // possibilities with cut off for ((*numPossibilities) = 0, i = 0; i < field->size; i++) { - // move possible ? if (field->board[i] == field->squareIsFree) { @@ -144,7 +143,6 @@ unsigned int *MiniMaxAI::getPossNormalMove(unsigned int *numPossibilities, void // 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++) { @@ -165,7 +163,6 @@ unsigned int *MiniMaxAI::getPossNormalMove(unsigned int *numPossibilities, void } } } else { - for ((*numPossibilities) = 0, from = 0; from < field->size; from++) { for (to = 0; to < field->size; to++) { @@ -387,12 +384,14 @@ inline void MiniMaxAI::updateWarning(unsigned int firstStone, unsigned int secon // 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->board[i] == field->oppPlayer->id) { atLeastOneStoneRemoveAble = true; break; } + if (!atLeastOneStoneRemoveAble) field->stoneMustBeRemoved = 0; } @@ -413,14 +412,12 @@ inline void MiniMaxAI::updatePossibleMoves(unsigned int stone, Player *stoneOwne // 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->board[neighbor] == field->squareIsFree) { - if (stoneRemoved) stoneOwner->numPossibleMoves--; else @@ -428,13 +425,11 @@ inline void MiniMaxAI::updatePossibleMoves(unsigned int stone, Player *stoneOwne // if there is a neighbour stone than it effects only this one } else if (field->board[neighbor] == field->curPlayer->id) { - if (stoneRemoved) field->curPlayer->numPossibleMoves++; else field->curPlayer->numPossibleMoves--; } else { - if (stoneRemoved) field->oppPlayer->numPossibleMoves++; else @@ -588,6 +583,7 @@ void MiniMaxAI::move(unsigned int threadNo, unsigned int idPossibility, bool opp // 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; diff --git a/src/perfect/miniMax.cpp b/src/perfect/miniMax.cpp index a2bd328a..ff98faf0 100644 --- a/src/perfect/miniMax.cpp +++ b/src/perfect/miniMax.cpp @@ -44,6 +44,7 @@ MiniMax::MiniMax() numWriteSkvOperations = 0; numReadPlyOperations = 0; numWritePlyOperations = 0; + if (MEASURE_ONLY_IO) { readSkvInterval.QuadPart = 0; writeSkvInterval.QuadPart = 0; @@ -90,6 +91,7 @@ bool MiniMax::falseOrStop() { if (stopOnCriticalError) WaitForSingleObject(GetCurrentProcess(), INFINITE); + return false; } @@ -145,7 +147,6 @@ void MiniMax::calculateDatabase(unsigned int maxDepthOfTree, bool onlyPrepareLay // when database not completed then do it if (hFileShortKnotValues != nullptr && skvfHeader.completed == false) { - // reserve memory lastCalculatedLayer.clear(); depthOfFullTree = maxDepthOfTree; @@ -186,8 +187,8 @@ void MiniMax::calculateDatabase(unsigned int maxDepthOfTree, bool onlyPrepareLay // don't save layer and header when only preparing layers or when if (onlyPrepareLayer) return; - if (!abortCalculation) { + if (!abortCalculation) { // calc layer statistics calcLayerStatistics("statistics.txt"); @@ -223,7 +224,6 @@ bool MiniMax::calcLayer(unsigned int layerNumber) // 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) diff --git a/src/perfect/miniMax.h b/src/perfect/miniMax.h index 9c7f8ecc..638d6ae4 100644 --- a/src/perfect/miniMax.h +++ b/src/perfect/miniMax.h @@ -292,25 +292,30 @@ public: 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) @@ -321,29 +326,34 @@ public: { 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) @@ -356,6 +366,7 @@ public: while (true) ; }; + virtual bool setSituation(unsigned int threadNo, unsigned int layerNum, unsigned int stateNumber) { while (true) @@ -386,11 +397,13 @@ public: ; 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) @@ -413,6 +426,7 @@ public: while (true) ; }; + virtual void wrapUpDatabaseCalculation(bool calculationAborted) { while (true) @@ -481,6 +495,7 @@ private: AlphaBetaDefaultThreadVars() { }; + AlphaBetaDefaultThreadVars(MiniMax *pMiniMax, AlphaBetaGlobalVars *alphaBetaVars, unsigned int layerNumber) { this->statesProcessed = 0; @@ -491,6 +506,7 @@ private: this->statsValueCounter[curStateValue] = 0; } }; + void reduceDefault() { pMiniMax->numStatesProcessed += this->statesProcessed; @@ -508,15 +524,18 @@ private: InitAlphaBetaVars() { }; + InitAlphaBetaVars(MiniMax *pMiniMax, AlphaBetaGlobalVars *alphaBetaVars, unsigned int layerNumber, BufferedFile *initArray, bool initAlreadyDone) : AlphaBetaDefaultThreadVars(pMiniMax, alphaBetaVars, layerNumber) { this->bufferedFile = initArray; this->initAlreadyDone = initAlreadyDone; }; + void initializeElement(InitAlphaBetaVars &master) { *this = master; }; + void reduce() { reduceDefault(); @@ -532,19 +551,23 @@ private: 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; @@ -592,6 +615,7 @@ private: RetroAnalysisDefaultThreadVars() { }; + RetroAnalysisDefaultThreadVars(MiniMax *pMiniMax, retroAnalysisGlobalVars *retroVars, unsigned int layerNumber) { this->statesProcessed = 0; @@ -602,6 +626,7 @@ private: this->statsValueCounter[curStateValue] = 0; } }; + void reduceDefault() { pMiniMax->numStatesProcessed += this->statesProcessed; @@ -619,15 +644,18 @@ private: InitRetroAnalysisVars() { }; + InitRetroAnalysisVars(MiniMax *pMiniMax, retroAnalysisGlobalVars *retroVars, unsigned int layerNumber, BufferedFile *initArray, bool initAlreadyDone) : RetroAnalysisDefaultThreadVars(pMiniMax, retroVars, layerNumber) { this->bufferedFile = initArray; this->initAlreadyDone = initAlreadyDone; }; + void initializeElement(InitRetroAnalysisVars &master) { *this = master; }; + void reduce() { reduceDefault(); @@ -641,13 +669,16 @@ private: AddNumSuccedorsVars() { }; + AddNumSuccedorsVars(MiniMax *pMiniMax, retroAnalysisGlobalVars *retroVars, unsigned int layerNumber) : RetroAnalysisDefaultThreadVars(pMiniMax, retroVars, layerNumber) { }; + void initializeElement(AddNumSuccedorsVars &master) { *this = master; }; + void reduce() { reduceDefault(); diff --git a/src/perfect/miniMaxAI.h b/src/perfect/miniMaxAI.h index 5d1bc271..27b39c31 100644 --- a/src/perfect/miniMaxAI.h +++ b/src/perfect/miniMaxAI.h @@ -86,52 +86,66 @@ protected: { 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 printBoard(unsigned int threadNo, unsigned char value) { }; + void prepareDatabaseCalculation() { }; + void wrapUpDatabaseCalculation(bool calculationAborted) { }; diff --git a/src/perfect/miniMaxWin.h b/src/perfect/miniMaxWin.h index 401232f8..32ac515b 100644 --- a/src/perfect/miniMaxWin.h +++ b/src/perfect/miniMaxWin.h @@ -22,9 +22,11 @@ public: virtual void setAlignment(wildWeasel::alignment &newAlignment) { }; + virtual void setVisibility(bool visible) { }; + virtual void setState(unsigned int curShowedLayer, MiniMax::StateNumberVarType curShowedState) { }; diff --git a/src/perfect/miniMax_alphaBetaAlgorithmn.cpp b/src/perfect/miniMax_alphaBetaAlgorithmn.cpp index 7bde5bfb..e0dc3832 100644 --- a/src/perfect/miniMax_alphaBetaAlgorithmn.cpp +++ b/src/perfect/miniMax_alphaBetaAlgorithmn.cpp @@ -106,6 +106,7 @@ bool MiniMax::initAlphaBeta(AlphaBetaGlobalVars &alphaBetaVars) // does initialization file exist ? CreateDirectoryA(ssInvArrayDirectory.str().c_str(), nullptr); invalidArray = new BufferedFile(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; @@ -216,6 +217,7 @@ DWORD MiniMax::initAlphaBetaThreadProc(void *pParameter, int index) return m->falseOrStop(); } } + iabVars->statsValueCounter[curStateValue]++; return TM_RETURN_VALUE_OK; @@ -251,6 +253,7 @@ bool MiniMax::runAlphaBeta(AlphaBetaGlobalVars &alphaBetaVars) case TM_RETURN_VALUE_UNEXPECTED_ERROR: return falseOrStop(); } + threadManager.setNumThreads(4); // reduce and delete thread specific data @@ -292,6 +295,7 @@ DWORD MiniMax::runAlphaBetaThreadProc(void *pParameter, int index) // 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; @@ -354,7 +358,6 @@ void MiniMax::letTheTreeGrow(Knot *knot, RunAlphaBetaVars *rabVars, unsigned int // 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); @@ -370,7 +373,6 @@ void MiniMax::letTheTreeGrow(Knot *knot, RunAlphaBetaVars *rabVars, unsigned int // movement is possible } else { - // move, letTreeGroe, undo alphaBetaTryPossibilites(knot, rabVars, tilLevel, idPossibility, pPossibilities, maxWonfreqValuesSubMoves, alpha, beta); @@ -407,7 +409,6 @@ bool MiniMax::alphaBetaTryDataBase(Knot *knot, RunAlphaBetaVars *rabVars, unsign // use database ? if (hFilePlyInfo != nullptr && hFileShortKnotValues != nullptr && (calcDatabase || layerInDatabase)) { - // situation already existend in database ? readKnotValueFromDatabase(rabVars->curThreadNo, layerNumber, stateNumber, shortKnotValue, invalidLayerOrStateNumber, subLayerInDatabaseAndCompleted); readPlyInfoFromDatabase(layerNumber, stateNumber, plyInfo); @@ -453,7 +454,6 @@ void MiniMax::alphaBetaTryPossibilites(Knot *knot, RunAlphaBetaVars *rabVars, un unsigned int curPoss; for (curPoss = 0; curPoss < knot->numPossibilities; curPoss++) { - // output if (tilLevel == depthOfFullTree && !calcDatabase) { printMoveInformation(rabVars->curThreadNo, idPossibility[curPoss], pPossibilities); @@ -491,6 +491,7 @@ void MiniMax::alphaBetaTryPossibilites(Knot *knot, RunAlphaBetaVars *rabVars, un // don't use alpha beta if using database if (hFileShortKnotValues != nullptr && calcDatabase) continue; + if (hFileShortKnotValues != nullptr && tilLevel + 1 >= depthOfFullTree) continue; @@ -581,7 +582,6 @@ void MiniMax::alphaBetaCalcPlyInfo(Knot *knot) // 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 @@ -600,9 +600,7 @@ void MiniMax::alphaBetaCalcPlyInfo(Knot *knot) // 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; @@ -638,7 +636,6 @@ void MiniMax::alphaBetaChooseBestMove(Knot *knot, RunAlphaBetaVars *rabVars, uns // 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++) { diff --git a/src/perfect/miniMax_database.cpp b/src/perfect/miniMax_database.cpp index 256705eb..72f362c2 100644 --- a/src/perfect/miniMax_database.cpp +++ b/src/perfect/miniMax_database.cpp @@ -318,7 +318,6 @@ void MiniMax::openPlyInfoFile(const char *directory) // invalid file ? if (dwBytesRead != sizeof(plyInfoHeader) || plyInfoHeader.headerCode != PLYINFO_HEADER_CODE) { - // create default header plyInfoHeader.plyInfoCompleted = false; plyInfoHeader.numLayers = getNumberOfLayers(); @@ -363,6 +362,7 @@ void MiniMax::saveLayerToFile(unsigned int layerNumber) // don't save layer and header when only preparing layers PlyInfo *myPis = &plyInfos[layerNumber]; LayerStats *myLss = &layerStats[layerNumber]; + if (onlyPrepareLayer) return; @@ -472,6 +472,7 @@ void MiniMax::readKnotValueFromDatabase(unsigned int layerNumber, unsigned int s 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]; @@ -488,6 +489,7 @@ void MiniMax::readKnotValueFromDatabase(unsigned int layerNumber, unsigned int s 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); } @@ -533,7 +535,6 @@ void MiniMax::readPlyInfoFromDatabase(unsigned int layerNumber, unsigned int sta 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); @@ -595,7 +596,6 @@ void MiniMax::saveKnotValueInDatabase(unsigned int layerNumber, unsigned int sta // 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 @@ -659,20 +659,23 @@ void MiniMax::savePlyInfoInDatabase(unsigned int layerNumber, unsigned int state // 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, ArrayInfo::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); } diff --git a/src/perfect/miniMax_retroAnalysis.cpp b/src/perfect/miniMax_retroAnalysis.cpp index 84de8095..cc72008c 100644 --- a/src/perfect/miniMax_retroAnalysis.cpp +++ b/src/perfect/miniMax_retroAnalysis.cpp @@ -39,6 +39,7 @@ bool MiniMax::calcKnotValuesByRetroAnalysis(vector &layersToCalcul 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; @@ -51,6 +52,7 @@ bool MiniMax::calcKnotValuesByRetroAnalysis(vector &layersToCalcul retroVars.totalNumKnots += layerStats[layerStats[layersToCalculate[curLayer]].succLayers[curSubLayer]].knotsInLayer; } } + retroVars.layerInitialized.assign(skvfHeader.numLayers, false); // output & filenames @@ -97,6 +99,7 @@ freeMem: SAFE_DELETE(retroVars.thread[threadNo].statesToProcess[plyCounter]); } } + for (curLayer = 0; curLayer < layersToCalculate.size(); curLayer++) { if (retroVars.countArrays[curLayer] != nullptr) { memoryUsed2 -= layerStats[layersToCalculate[curLayer]].knotsInLayer * sizeof(CountArrayVarType); @@ -104,8 +107,10 @@ freeMem: } SAFE_DELETE_ARRAY(retroVars.countArrays[curLayer]); } + if (!abortCalculation) PRINT(2, this, " Bytes in memory: " << memoryUsed2); + return !abortCalculation; } @@ -125,7 +130,6 @@ bool MiniMax::initRetroAnalysis(retroAnalysisGlobalVars &retroVars) // 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; @@ -178,6 +182,7 @@ bool MiniMax::initRetroAnalysis(retroAnalysisGlobalVars &retroVars) initAlreadyDone = false; initArray->flushBuffers(); SAFE_DELETE(initArray); + if (numStatesProcessed < layerStats[layerNumber].knotsInLayer) return falseOrStop(); @@ -225,7 +230,6 @@ DWORD MiniMax::initRetroAnalysisThreadProc(void *pParameter, int index) // initialization not done } else { - // set current selected situation if (!m->setSituation(iraVars->curThreadNo, curState.layerNumber, curState.stateNumber)) { curStateValue = SKV_VALUE_INVALID; @@ -237,13 +241,11 @@ DWORD MiniMax::initRetroAnalysisThreadProc(void *pParameter, int index) // 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); @@ -260,6 +262,7 @@ DWORD MiniMax::initRetroAnalysisThreadProc(void *pParameter, int index) return m->falseOrStop(); } } + iraVars->statsValueCounter[curStateValue]++; return TM_RETURN_VALUE_OK; @@ -287,6 +290,7 @@ bool MiniMax::prepareCountArrays(retroAnalysisGlobalVars &retroVars) // 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); @@ -294,6 +298,7 @@ bool MiniMax::prepareCountArrays(retroAnalysisGlobalVars &retroVars) // prepare count arrays CreateDirectoryA(ssCountArrayPath.str().c_str(), nullptr); + if ((hFileCountArray = CreateFileA(ssCountArrayFilePath.str().c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr)) == INVALID_HANDLE_VALUE) { PRINT(0, this, "ERROR: Could not open File " << ssCountArrayFilePath.str() << "!"); return falseOrStop(); @@ -344,6 +349,7 @@ bool MiniMax::prepareCountArrays(retroAnalysisGlobalVars &retroVars) if (dwWritten != numKnotsInCurLayer * sizeof(CountArrayVarType)) return falseOrStop(); } + PRINT(2, this, " Count array saved to file: " << ssCountArrayFilePath.str()); } @@ -496,7 +502,6 @@ DWORD MiniMax::addNumSuccedorsThreadProc(void *pParameter, int index) // iteration for (curPred = 0; curPred < amountOfPred; curPred++) { - // current predecessor predState.layerNumber = ansVars->predVars[curPred].predLayerNumbers; predState.stateNumber = ansVars->predVars[curPred].predStateNumbers; @@ -506,6 +511,7 @@ DWORD MiniMax::addNumSuccedorsThreadProc(void *pParameter, int index) if (ansVars->retroVars->layersToCalculate[curLayerId] == predState.layerNumber) break; } + if (curLayerId == numLayersToCalculate) continue; @@ -577,6 +583,7 @@ bool MiniMax::performRetroAnalysis(retroAnalysisGlobalVars &retroVars) // 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); @@ -586,6 +593,7 @@ bool MiniMax::performRetroAnalysis(retroAnalysisGlobalVars &retroVars) savePlyInfoInDatabase(curState.layerNumber, curState.stateNumber, PLYINFO_VALUE_INVALID); } } + PRINT(1, this, " *** Iteration finished! ***"); // every thing ok @@ -624,7 +632,6 @@ DWORD MiniMax::performRetroAnalysisThreadProc(void *pParameter) // skip empty and uninitialized cyclic arrays if (threadVars->statesToProcess[curNumPlies] != nullptr) { - if (threadNo == 0) { PRINT(0, m, " Current number of plies: " << (unsigned int)curNumPlies << "/" << threadVars->statesToProcess.size()); for (threadCounter = 0; threadCounter < m->threadManager.getNumThreads(); threadCounter++) { @@ -633,7 +640,6 @@ DWORD MiniMax::performRetroAnalysisThreadProc(void *pParameter) } while (threadVars->statesToProcess[curNumPlies]->takeBytes(sizeof(StateAdress), (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"); @@ -671,7 +677,6 @@ DWORD MiniMax::performRetroAnalysisThreadProc(void *pParameter) // iteration for (curPred = 0; curPred < amountOfPred; curPred++) { - // current predecessor predState.layerNumber = predVars[curPred].predLayerNumbers; predState.stateNumber = predVars[curPred].predStateNumbers; @@ -689,7 +694,6 @@ DWORD MiniMax::performRetroAnalysisThreadProc(void *pParameter) // 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); @@ -780,5 +784,6 @@ bool MiniMax::addStateToProcessQueue(retroAnalysisGlobalVars &retroVars, RetroAn // everything was fine threadVars.numStatesToProcess++; + return true; } diff --git a/src/perfect/miniMax_statistics.cpp b/src/perfect/miniMax_statistics.cpp index c14e396d..31142041 100644 --- a/src/perfect/miniMax_statistics.cpp +++ b/src/perfect/miniMax_statistics.cpp @@ -141,7 +141,6 @@ void MiniMax::showLayerStats(unsigned int layerNumber) // 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]++; @@ -220,7 +219,6 @@ bool MiniMax::calcLayerStatistics(char *statisticsFileName) // 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 @@ -321,6 +319,7 @@ void MiniMax::ArrayInfoContainer::addArray(unsigned int layerNumber, unsigned in { // create new info object and add to list EnterCriticalSection(&c->csOsPrint); + ArrayInfo ais; ais.belongsToLayer = layerNumber; ais.compressedSizeInBytes = compressedSize; @@ -342,6 +341,7 @@ void MiniMax::ArrayInfoContainer::addArray(unsigned int layerNumber, unsigned in if (c->userPrintFunc != nullptr) { c->userPrintFunc(c->pDataForUserPrintFunc); } + LeaveCriticalSection(&c->csOsPrint); } @@ -378,6 +378,7 @@ void MiniMax::ArrayInfoContainer::removeArray(unsigned int layerNumber, unsigned if (c->userPrintFunc != nullptr) { c->userPrintFunc(c->pDataForUserPrintFunc); } + LeaveCriticalSection(&c->csOsPrint); } @@ -395,6 +396,7 @@ void MiniMax::ArrayInfoContainer::updateArray(unsigned int layerNumber, unsigned // notify cahnge EnterCriticalSection(&c->csOsPrint); + ArrayInfoChange aic; aic.arrayInfo = &(*itr); aic.itemIndex = (unsigned int)std::distance(listArrays.begin(), itr); @@ -405,6 +407,7 @@ void MiniMax::ArrayInfoContainer::updateArray(unsigned int layerNumber, unsigned c->userPrintFunc(c->pDataForUserPrintFunc); } itr->updateCounter = 0; + LeaveCriticalSection(&c->csOsPrint); } } \ No newline at end of file diff --git a/src/perfect/miniMax_test.cpp b/src/perfect/miniMax_test.cpp index 90f0888c..19c10c9f 100644 --- a/src/perfect/miniMax_test.cpp +++ b/src/perfect/miniMax_test.cpp @@ -35,6 +35,7 @@ bool MiniMax::testLayer(unsigned int layerNumber) 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; @@ -161,7 +162,6 @@ DWORD MiniMax::testLayerThreadProc(void *pParameter, int index) goto errorInDatabase; } } else { - // check each possible move for (i = 0; i < numPossibilities; i++) { @@ -194,7 +194,6 @@ DWORD MiniMax::testLayerThreadProc(void *pParameter, int index) // 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) { @@ -231,7 +230,6 @@ DWORD MiniMax::testLayerThreadProc(void *pParameter, int index) 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; } @@ -263,7 +261,6 @@ DWORD MiniMax::testLayerThreadProc(void *pParameter, int index) 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; } @@ -306,6 +303,7 @@ DWORD MiniMax::testLayerThreadProc(void *pParameter, int index) break; } } + return TM_RETURN_VALUE_OK; errorInDatabase: @@ -360,6 +358,7 @@ bool MiniMax::testSetSituationAndGetPoss(unsigned int layerNumber) 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; @@ -372,6 +371,7 @@ bool MiniMax::testSetSituationAndGetPoss(unsigned int layerNumber) // 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: @@ -430,6 +430,7 @@ DWORD MiniMax::testSetSituationThreadProc(void *pParameter, int 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"); @@ -481,6 +482,7 @@ DWORD MiniMax::testSetSituationThreadProc(void *pParameter, int index) m->setSituation(tlVars->curThreadNo, curState.layerNumber, curState.stateNumber); } } + return TM_RETURN_VALUE_OK; //errorInDatabase: @@ -525,7 +527,6 @@ bool MiniMax::testIfSymStatesHaveSameValue(unsigned int 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"); @@ -549,7 +550,6 @@ bool MiniMax::testIfSymStatesHaveSameValue(unsigned int layerNumber) // save value for all symmetric states for (i = 0; i < numSymmetricStates; i++) { - readKnotValueFromDatabase(layerNumber, symStateNumbers[i], shortValueOfSymState); readPlyInfoFromDatabase(layerNumber, symStateNumbers[i], numPliesTillSymState); @@ -571,6 +571,7 @@ bool MiniMax::testIfSymStatesHaveSameValue(unsigned int layerNumber) // layer is ok PRINT(0, this, "TEST PASSED !"); + return true; errorInDatabase: @@ -578,5 +579,6 @@ errorInDatabase: // layer is not ok if (layerNumber) PRINT(0, this, "DATABASE ERROR IN LAYER " << layerNumber << " AND STATE " << stateNumber); + return falseOrStop(); } diff --git a/src/perfect/perfect.vcxproj b/src/perfect/perfect.vcxproj index bb9cb229..84c4f898 100644 --- a/src/perfect/perfect.vcxproj +++ b/src/perfect/perfect.vcxproj @@ -154,7 +154,7 @@ - + @@ -172,7 +172,7 @@ - + diff --git a/src/perfect/perfect.vcxproj.filters b/src/perfect/perfect.vcxproj.filters index 8f88e746..c5093a4c 100644 --- a/src/perfect/perfect.vcxproj.filters +++ b/src/perfect/perfect.vcxproj.filters @@ -51,7 +51,7 @@ Header Files - + Header Files @@ -98,10 +98,10 @@ Source Files - + Source Files - + Source Files diff --git a/src/perfect/perfectAI.cpp b/src/perfect/perfectAI.cpp index d9143077..a269dc03 100644 --- a/src/perfect/perfectAI.cpp +++ b/src/perfect/perfectAI.cpp @@ -8,149 +8,166 @@ #include "perfectAI.h" + unsigned int soTableTurnLeft[] = { - 2, 14, 23, - 5, 13, 20, - 8, 12, 17, - 1, 4, 7, 16, 19, 22, - 6, 11, 15, - 3, 10, 18, - 0, 9, 21 }; + 2, 14, 23, + 5, 13, 20, + 8,12,17, + 1, 4, 7, 16,19,22, + 6,11,15, + 3, 10, 18, + 0, 9, 21 +}; unsigned int soTableDoNothing[] = { - 0, 1, 2, - 3, 4, 5, - 6, 7, 8, - 9, 10, 11, 12, 13, 14, - 15, 16, 17, - 18, 19, 20, - 21, 22, 23 }; + 0, 1, 2, + 3, 4, 5, + 6, 7, 8, + 9,10,11, 12,13,14, + 15,16,17, + 18, 19, 20, +21, 22, 23 +}; unsigned int soTableMirrorHori[] = { - 21, 22, 23, - 18, 19, 20, - 15, 16, 17, - 9, 10, 11, 12, 13, 14, - 6, 7, 8, - 3, 4, 5, - 0, 1, 2 }; +21, 22, 23, + 18, 19, 20, + 15,16,17, + 9,10,11, 12,13,14, + 6, 7, 8, + 3, 4, 5, + 0, 1, 2 +}; unsigned int soTableTurn180[] = { - 23, 22, 21, - 20, 19, 18, - 17, 16, 15, - 14, 13, 12, 11, 10, 9, - 8, 7, 6, - 5, 4, 3, - 2, 1, 0 }; + 23, 22, 21, + 20, 19, 18, + 17,16,15, + 14,13,12, 11,10, 9, + 8, 7, 6, + 5, 4, 3, + 2, 1, 0 +}; unsigned int soTableInvert[] = { - 6, 7, 8, - 3, 4, 5, - 0, 1, 2, - 11, 10, 9, 14, 13, 12, - 21, 22, 23, - 18, 19, 20, - 15, 16, 17 }; + 6, 7, 8, + 3, 4, 5, + 0, 1, 2, + 11,10, 9, 14,13,12, + 21,22,23, + 18, 19, 20, + 15, 16, 17 +}; unsigned int soTableInvMirHori[] = { - 15, 16, 17, - 18, 19, 20, - 21, 22, 23, - 11, 10, 9, 14, 13, 12, - 0, 1, 2, - 3, 4, 5, - 6, 7, 8 }; + 15, 16, 17, + 18, 19, 20, + 21,22,23, + 11,10, 9, 14,13,12, + 0, 1, 2, + 3, 4, 5, + 6, 7, 8 +}; unsigned int soTableInvMirVert[] = { - 8, 7, 6, - 5, 4, 3, - 2, 1, 0, - 12, 13, 14, 9, 10, 11, - 23, 22, 21, - 20, 19, 18, - 17, 16, 15 }; + 8, 7, 6, + 5, 4, 3, + 2, 1, 0, + 12,13,14, 9,10,11, + 23,22,21, + 20, 19, 18, + 17, 16, 15 +}; unsigned int soTableInvMirDiag1[] = { - 17, 12, 8, - 20, 13, 5, - 23, 14, 2, - 16, 19, 22, 1, 4, 7, - 21, 9, 0, - 18, 10, 3, - 15, 11, 6 }; + 17, 12, 8, + 20, 13, 5, + 23,14, 2, + 16,19,22, 1, 4, 7, + 21, 9, 0, + 18, 10, 3, + 15, 11, 6 +}; unsigned int soTableInvMirDiag2[] = { - 6, 11, 15, - 3, 10, 18, - 0, 9, 21, - 7, 4, 1, 22, 19, 16, - 2, 14, 23, - 5, 13, 20, - 8, 12, 17 }; + 6, 11, 15, + 3, 10, 18, + 0, 9,21, + 7, 4, 1, 22,19,16, + 2,14,23, + 5, 13, 20, + 8, 12, 17 +}; unsigned int soTableInvLeft[] = { - 8, 12, 17, - 5, 13, 20, - 2, 14, 23, - 7, 4, 1, 22, 19, 16, - 0, 9, 21, - 3, 10, 18, - 6, 11, 15 }; + 8, 12, 17, + 5, 13, 20, + 2,14,23, + 7, 4, 1, 22,19,16, + 0, 9,21, + 3, 10, 18, + 6, 11, 15 +}; unsigned int soTableInvRight[] = { - 15, 11, 6, - 18, 10, 3, - 21, 9, 0, - 16, 19, 22, 1, 4, 7, - 23, 14, 2, - 20, 13, 5, - 17, 12, 8 }; + 15, 11, 6, + 18, 10, 3, + 21, 9, 0, + 16,19,22, 1, 4, 7, + 23,14, 2, + 20, 13, 5, + 17, 12, 8 +}; unsigned int soTableInv180[] = { - 17, 16, 15, - 20, 19, 18, - 23, 22, 21, - 12, 13, 14, 9, 10, 11, - 2, 1, 0, - 5, 4, 3, - 8, 7, 6 }; + 17, 16, 15, + 20, 19, 18, + 23,22,21, + 12,13,14, 9,10,11, + 2, 1, 0, + 5, 4, 3, + 8, 7, 6 +}; unsigned int soTableMirrorDiag1[] = { - 0, 9, 21, - 3, 10, 18, - 6, 11, 15, - 1, 4, 7, 16, 19, 22, - 8, 12, 17, - 5, 13, 20, - 2, 14, 23 }; + 0, 9, 21, + 3, 10, 18, + 6,11,15, + 1, 4, 7, 16,19,22, + 8,12,17, + 5, 13, 20, + 2, 14, 23 +}; unsigned int soTableTurnRight[] = { - 21, 9, 0, - 18, 10, 3, - 15, 11, 6, - 22, 19, 16, 7, 4, 1, - 17, 12, 8, - 20, 13, 5, - 23, 14, 2 }; + 21, 9, 0, + 18, 10, 3, + 15,11, 6, + 22,19,16, 7, 4, 1, + 17,12, 8, + 20, 13, 5, + 23, 14, 2 +}; unsigned int soTableMirrorVert[] = { - 2, 1, 0, - 5, 4, 3, - 8, 7, 6, - 14, 13, 12, 11, 10, 9, - 17, 16, 15, - 20, 19, 18, - 23, 22, 21 }; + 2, 1, 0, + 5, 4, 3, + 8, 7, 6, + 14,13,12, 11,10, 9, + 17,16,15, + 20, 19, 18, + 23, 22, 21 +}; unsigned int soTableMirrorDiag2[] = { - 23, 14, 2, - 20, 13, 5, - 17, 12, 8, - 22, 19, 16, 7, 4, 1, - 15, 11, 6, - 18, 10, 3, - 21, 9, 0 }; + 23, 14, 2, + 20, 13, 5, + 17,12, 8, + 22,19,16, 7, 4, 1, + 15,11, 6, + 18, 10, 3, + 21, 9, 0 +}; // define the four groups unsigned int squareIndexGroupA[] = { 3, 5, 20, 18 }; @@ -187,6 +204,7 @@ PerfectAI::PerfectAI(const char *directory) // threadVars = new ThreadVars[getNumThreads()]; + for (unsigned int curThread = 0; curThread < getNumThreads(); curThread++) { threadVars[curThread].parent = this; threadVars[curThread].field = &dummyField; @@ -199,13 +217,13 @@ PerfectAI::PerfectAI(const char *directory) if (strlen(directory) && PathFileExistsA(directory)) { ssPreCalcVarsFilePath << directory << "\\"; } + ssPreCalcVarsFilePath << "preCalculatedVars.dat"; hFilePreCalcVars = CreateFileA(ssPreCalcVarsFilePath.str().c_str(), GENERIC_READ /*| GENERIC_WRITE*/, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); ReadFile(hFilePreCalcVars, &preCalcVarsHeader, sizeof(PreCalcedVarsFileHeader), &dwBytesRead, nullptr); // vars already stored in file? if (dwBytesRead) { - // Read from file ReadFile(hFilePreCalcVars, layer, sizeof(Layer) * NUM_LAYERS, &dwBytesRead, nullptr); ReadFile(hFilePreCalcVars, layerIndex, sizeof(unsigned int) * 2 * NUM_STONES_PER_PLAYER_PLUS_ONE * NUM_STONES_PER_PLAYER_PLUS_ONE, &dwBytesRead, nullptr); @@ -308,7 +326,6 @@ PerfectAI::PerfectAI(const char *directory) // concatenated symmetry operations for (a = 0; a < NUM_SYM_OPERATIONS; a++) { for (b = 0; b < NUM_SYM_OPERATIONS; b++) { - // test each symmetry operation for (c = 0; c < NUM_SYM_OPERATIONS; c++) { @@ -353,7 +370,6 @@ PerfectAI::PerfectAI(const char *directory) indexAB[stateAB] = NOT_INDEXED; for (stateAB = 0; stateAB < MAX_ANZ_STELLUNGEN_A * MAX_ANZ_STELLUNGEN_B; stateAB++) { - // new state ? if (indexAB[stateAB] == NOT_INDEXED) { @@ -399,7 +415,8 @@ PerfectAI::PerfectAI(const char *directory) for (b = 0; b <= NUM_STONES_PER_PLAYER; b++) { if (a + b > numSquaresGroupC + numSquaresGroupD) continue; - originalStateCD_tmp[a][b] = new unsigned int[mOverN[numSquaresGroupC + numSquaresGroupD][a] * mOverN[numSquaresGroupC + numSquaresGroupD - a][b]]; + originalStateCD_tmp[a][b] = + new unsigned int[mOverN[numSquaresGroupC + numSquaresGroupD][a] * mOverN[numSquaresGroupC + numSquaresGroupD - a][b]]; anzahlStellungenCD[a][b] = 0; } } @@ -408,7 +425,6 @@ PerfectAI::PerfectAI(const char *directory) memset(indexCD, NOT_INDEXED, 4 * MAX_ANZ_STELLUNGEN_C * MAX_ANZ_STELLUNGEN_D); for (stateCD = 0; stateCD < MAX_ANZ_STELLUNGEN_C * MAX_ANZ_STELLUNGEN_D; stateCD++) { - // new state ? if (indexCD[stateCD] == NOT_INDEXED) { @@ -2289,6 +2305,7 @@ void PerfectAI::getPredecessors(unsigned int threadNo, unsigned int *amountOfPre } } } + } else if (!tv->gameHasFinished) { // test each destination @@ -2371,7 +2388,6 @@ void PerfectAI::getPredecessors(unsigned int threadNo, unsigned int *amountOfPre // stone mustn't be part of mill if ((!(tv->field->board[tv->field->neighbour[from][0][0]] == tv->field->curPlayer->id && tv->field->board[tv->field->neighbour[from][0][1]] == tv->field->curPlayer->id)) && (!(tv->field->board[tv->field->neighbour[from][1][0]] == tv->field->curPlayer->id && tv->field->board[tv->field->neighbour[from][1][1]] == tv->field->curPlayer->id))) { - // put back stone tv->field->stoneMustBeRemoved = 1; tv->field->board[from] = tv->field->curPlayer->id; @@ -2456,12 +2472,14 @@ bool PerfectAI::checkMoveAndSetSituation() // count completed mills numberOfMillsCurrentPlayer = 0; numberOfMillsOpponentPlayer = 0; + for (i = 0; i < fieldStruct::size; i++) { if (tv->field->board[i] == tv->field->curPlayer->id) numberOfMillsCurrentPlayer += tv->field->stonePartOfMill[i]; else numberOfMillsOpponentPlayer += tv->field->stonePartOfMill[i]; } + numberOfMillsCurrentPlayer /= 3; numberOfMillsOpponentPlayer /= 3; @@ -2634,13 +2652,17 @@ bool PerfectAI::checkGetPredThanGetPoss() // perform several commands to see in debug mode where the error occurs for (k = 0; k < tv->field->size; k++) symField[k] = tv->field->board[k]; + applySymmetrieOperationOnField(reverseSymOperation[predVars[j].predSymOperation], (unsigned int *)symField, (unsigned int *)tv->field->board); + for (k = 0; k < tv->field->size; k++) symField[k] = tv->field->stonePartOfMill[k]; + applySymmetrieOperationOnField(reverseSymOperation[predVars[j].predSymOperation], (unsigned int *)symField, (unsigned int *)tv->field->stonePartOfMill); cout << "predecessor" << endl; cout << " layerNum: " << predVars[j].predLayerNumbers << "\tstateNum: " << predVars[j].predStateNumbers << endl; printBoard(threadNo, 0); + if (predVars[j].playerToMoveChanged) { k = tv->field->curPlayer->id; tv->field->curPlayer->id = tv->field->oppPlayer->id; @@ -2648,6 +2670,7 @@ bool PerfectAI::checkGetPredThanGetPoss() for (k = 0; k < tv->field->size; k++) tv->field->board[k] = -1 * tv->field->board[k]; } + idPossibility = getPossibilities(threadNo, &numPossibilities, &isOpponentLevel, &pPossibilities); setSituation(threadNo, layerNum, stateNum); cout << "current state" << endl; @@ -2659,10 +2682,14 @@ bool PerfectAI::checkGetPredThanGetPoss() // regard used symmetry operation for (k = 0; k < tv->field->size; k++) symField[k] = tv->field->board[k]; + applySymmetrieOperationOnField(reverseSymOperation[predVars[j].predSymOperation], (unsigned int *)symField, (unsigned int *)tv->field->board); + for (k = 0; k < tv->field->size; k++) symField[k] = tv->field->stonePartOfMill[k]; + applySymmetrieOperationOnField(reverseSymOperation[predVars[j].predSymOperation], (unsigned int *)symField, (unsigned int *)tv->field->stonePartOfMill); + if (predVars[j].playerToMoveChanged) { k = tv->field->curPlayer->id; tv->field->curPlayer->id = tv->field->oppPlayer->id; @@ -2676,7 +2703,6 @@ bool PerfectAI::checkGetPredThanGetPoss() // go to each successor state for (i = 0; i < numPossibilities; i++) { - // move move(threadNo, idPossibility[i], isOpponentLevel, &pBackup, pPossibilities); @@ -2693,22 +2719,28 @@ bool PerfectAI::checkGetPredThanGetPoss() // error? if (i == numPossibilities) { - cout << endl << "ERROR: Not all predecessors lead to state " << stateNum << " calling move()" << endl; //return false; // perform several commands to see in debug mode where the error occurs setSituation(threadNo, predVars[j].predLayerNumbers, predVars[j].predStateNumbers); + for (k = 0; k < tv->field->size; k++) symField[k] = tv->field->board[k]; + applySymmetrieOperationOnField(reverseSymOperation[predVars[j].predSymOperation], (unsigned int *)symField, (unsigned int *)tv->field->board); + for (k = 0; k < tv->field->size; k++) symField[k] = tv->field->stonePartOfMill[k]; + applySymmetrieOperationOnField(reverseSymOperation[predVars[j].predSymOperation], (unsigned int *)symField, (unsigned int *)tv->field->stonePartOfMill); + cout << "predecessor" << endl; cout << " layerNum: " << predVars[j].predLayerNumbers << "\tstateNum: " << predVars[j].predStateNumbers << endl; + printBoard(threadNo, 0); + if (predVars[j].playerToMoveChanged) { k = tv->field->curPlayer->id; tv->field->curPlayer->id = tv->field->oppPlayer->id; @@ -2716,6 +2748,7 @@ bool PerfectAI::checkGetPredThanGetPoss() for (k = 0; k < tv->field->size; k++) tv->field->board[k] = -1 * tv->field->board[k]; } + idPossibility = getPossibilities(threadNo, &numPossibilities, &isOpponentLevel, &pPossibilities); setSituation(threadNo, layerNum, stateNum); cout << "current state" << endl; @@ -2726,15 +2759,22 @@ bool PerfectAI::checkGetPredThanGetPoss() k = tv->field->curPlayer->id; tv->field->curPlayer->id = tv->field->oppPlayer->id; tv->field->oppPlayer->id = k; + for (k = 0; k < tv->field->size; k++) tv->field->board[k] = -1 * tv->field->board[k]; + setSituation(threadNo, predVars[j].predLayerNumbers, predVars[j].predStateNumbers); + for (k = 0; k < tv->field->size; k++) symField[k] = tv->field->board[k]; + applySymmetrieOperationOnField(reverseSymOperation[predVars[j].predSymOperation], (unsigned int *)symField, (unsigned int *)tv->field->board); + for (k = 0; k < tv->field->size; k++) symField[k] = tv->field->stonePartOfMill[k]; + applySymmetrieOperationOnField(reverseSymOperation[predVars[j].predSymOperation], (unsigned int *)symField, (unsigned int *)tv->field->stonePartOfMill); + printBoard(threadNo, 0); idPossibility = getPossibilities(threadNo, &numPossibilities, &isOpponentLevel, &pPossibilities); move(threadNo, idPossibility[1], isOpponentLevel, &pBackup, pPossibilities); diff --git a/src/perfect/perfectAI.h b/src/perfect/perfectAI.h index 73586f16..96d1d1ed 100644 --- a/src/perfect/perfectAI.h +++ b/src/perfect/perfectAI.h @@ -186,6 +186,7 @@ protected: void calcPossibleMoves(Player *player); void storePredecessor(unsigned int numberOfMillsCurrentPlayer, unsigned int numberOfMillsOpponentPlayer, unsigned int *amountOfPred, RetroAnalysisPredVars *predVars); }; + ThreadVars *threadVars; // database functions diff --git a/src/perfect/randomAI.cpp b/src/perfect/randomAI.cpp index 0b7dcaf0..4dd8568c 100644 --- a/src/perfect/randomAI.cpp +++ b/src/perfect/randomAI.cpp @@ -39,7 +39,6 @@ void RandomAI::play(fieldStruct *theField, unsigned int *pushFrom, unsigned int // must stone be removed ? if (theField->stoneMustBeRemoved) { - // search a stone from the enemy do { from = rand() % theField->size; @@ -48,7 +47,6 @@ void RandomAI::play(fieldStruct *theField, unsigned int *pushFrom, unsigned int // still in setting phase ? } else if (theField->settingPhase) { - // search a free square do { from = theField->size; @@ -57,7 +55,6 @@ void RandomAI::play(fieldStruct *theField, unsigned int *pushFrom, unsigned int // try to push randomly } else { - do { // search an own stone do { diff --git a/src/perfect/strLib.cpp b/src/perfect/strLib.cpp index 538dd661..f28fa429 100644 --- a/src/perfect/strLib.cpp +++ b/src/perfect/strLib.cpp @@ -58,10 +58,12 @@ MyString::~MyString() delete[] strA; strA = nullptr; } + if (strW != nullptr) { delete[] strW; strW = nullptr; } + strW = nullptr; strA = nullptr; length = 0; @@ -99,8 +101,10 @@ MyString &MyString::assign(const char *cStr) if (reserved < newReserved) this->~MyString(); + if (strA == nullptr) strA = new char[newReserved]; + if (strW == nullptr) strW = new WCHAR[newReserved]; @@ -126,8 +130,10 @@ MyString &MyString::assign(const WCHAR *cStr) if (reserved < newReserved) this->~MyString(); + if (strA == nullptr) strA = new char[newReserved]; + if (strW == nullptr) strW = new WCHAR[newReserved]; @@ -179,7 +185,6 @@ bool readAsciiData(HANDLE hFile, double *pData, unsigned int numValues, unsigned // read each value do { - // read from buffer if necessary if (curBufferPos >= bufferSize - maxValueLengthInBytes) { memcpy(&buffer[0], &buffer[curBufferPos], bufferSize - curBufferPos); @@ -352,9 +357,11 @@ bool readAsciiData(HANDLE hFile, double *pData, unsigned int numValues, unsigned if (decimalPos) { (*pData) += fractionalValue * fractionalFactor[decimalPos]; } + if (valIsNegativ) { (*pData) *= -1; } + if (exponent) { (*pData) *= pow(10, expIsNegativ ? -1 * exponentialValue : 1); } diff --git a/src/perfect/threadManager.cpp b/src/perfect/threadManager.cpp index 7ca9d361..bffa9af1 100644 --- a/src/perfect/threadManager.cpp +++ b/src/perfect/threadManager.cpp @@ -58,9 +58,11 @@ ThreadManager::~ThreadManager() if (hBarrier != nullptr) delete[] hBarrier; hBarrier = nullptr; + if (hThread != nullptr) delete[] hThread; hThread = nullptr; + if (threadId != nullptr) delete[] threadId; threadId = nullptr; @@ -127,18 +129,24 @@ bool ThreadManager::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(nullptr, false, false, nullptr); } + LeaveCriticalSection(&csBarrier); + return true; } @@ -149,7 +157,6 @@ bool ThreadManager::setNumThreads(unsigned int newNumThreads) void ThreadManager::pauseExecution() { for (unsigned int curThread = 0; curThread < numThreads; curThread++) { - // unsuspend all threads if (!executionPaused) { SuspendThread(hThread[curThread]); @@ -158,6 +165,7 @@ void ThreadManager::pauseExecution() ResumeThread(hThread[curThread]); } } + executionPaused = (!executionPaused); } @@ -170,6 +178,7 @@ void ThreadManager::cancelExecution() { termineAllThreads = true; executionCancelled = true; + if (executionPaused) { pauseExecution(); } @@ -208,6 +217,7 @@ unsigned int ThreadManager::getThreadNumber() return curThreadNo; } } + return 0; } @@ -284,12 +294,16 @@ unsigned int ThreadManager::executeParallelLoop(DWORD threadProc(void *pParamete // parameters ok? if (executionCancelled == true) return TM_RETURN_VALUE_EXECUTION_CANCELLED; + if (pParameter == nullptr) 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; @@ -305,7 +319,6 @@ unsigned int ThreadManager::executeParallelLoop(DWORD threadProc(void *pParamete // create threads for (curThreadNo = 0; curThreadNo < numThreads; curThreadNo++) { - forLoopParameters[curThreadNo].pParameter = (pParameter != nullptr ? (void *)(((char *)pParameter) + curThreadNo * parameterStructSize) : nullptr); forLoopParameters[curThreadNo].threadManager = this; forLoopParameters[curThreadNo].threadProc = threadProc; @@ -335,14 +348,18 @@ unsigned int ThreadManager::executeParallelLoop(DWORD threadProc(void *pParamete // create suspended thread hThread[curThreadNo] = CreateThread(nullptr, dwStackSize, threadForLoop, (LPVOID)(&forLoopParameters[curThreadNo]), CREATE_SUSPENDED, &threadId[curThreadNo]); + SetThreadPriority(hThread[curThreadNo], THREAD_PRIORITY_BELOW_NORMAL); + if (hThread[curThreadNo] == nullptr) { for (curThreadNo; curThreadNo > 0; curThreadNo--) { CloseHandle(hThread[curThreadNo - 1]); hThread[curThreadNo - 1] = nullptr; } + return TM_RETURN_VALUE_UNEXPECTED_ERROR; } + //DWORD dwThreadAffinityMask = 1 << curThreadNo; //SetThreadAffinityMask(hThread[curThreadNo], &dwThreadAffinityMask); } diff --git a/src/perfect/threadManager.h b/src/perfect/threadManager.h index 49613390..45a8c454 100644 --- a/src/perfect/threadManager.h +++ b/src/perfect/threadManager.h @@ -78,9 +78,11 @@ public: virtual void initializeElement() { }; + virtual void destroyElement() { }; + virtual void reduce() { };