From e89be5e8833c74855d3c29bf2f23c774a8a221b0 Mon Sep 17 00:00:00 2001 From: CalciteM Team Date: Sun, 14 Jul 2019 11:04:51 +0800 Subject: [PATCH] =?UTF-8?q?hashmap=20cpp=20=E6=8D=A2=E4=B8=BA=20h?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- NineChess/ninechess.pro | 2 +- NineChess/ninechess.vcxproj | 4 +- NineChess/ninechess.vcxproj.filters | 8 +- NineChess/src/HashNode.h | 155 ++++++++++++++++++++ NineChess/src/aithread.cpp | 2 +- NineChess/src/hashmap.cpp | 2 + NineChess/src/hashmap.h | 214 +++++++++++----------------- NineChess/src/ninechess.cpp | 25 ++-- NineChess/src/ninechess.h | 7 - NineChess/src/ninechessai_ab.cpp | 13 +- NineChess/src/ninechessai_ab.h | 14 +- 11 files changed, 266 insertions(+), 180 deletions(-) create mode 100644 NineChess/src/HashNode.h diff --git a/NineChess/ninechess.pro b/NineChess/ninechess.pro index 9d19cf0f..79161391 100644 --- a/NineChess/ninechess.pro +++ b/NineChess/ninechess.pro @@ -37,7 +37,7 @@ HEADERS += \ src/gamescene.h \ src/gameview.h \ src/graphicsconst.h \ - src/hashmap.h \ + src/hashMap.h \ src/ninechess.h \ src/ninechessai_ab.h \ src/ninechesswindow.h \ diff --git a/NineChess/ninechess.vcxproj b/NineChess/ninechess.vcxproj index f9a59b74..b57f59bd 100644 --- a/NineChess/ninechess.vcxproj +++ b/NineChess/ninechess.vcxproj @@ -378,7 +378,6 @@ - @@ -427,7 +426,8 @@ - + + .\GeneratedFiles\$(ConfigurationName);.\GeneratedFiles;.;D:\Qt\Qt5.11.0\5.11.0\msvc2017_64\include;debug;\include;D:\Qt\Qt5.11.0\5.11.0\msvc2017_64\mkspecs\win32-msvc;D:\Qt\Qt5.11.0\5.11.0\msvc2017_64\include\QtWidgets;D:\Qt\Qt5.11.0\5.11.0\msvc2017_64\include\QtCore;D:\Qt\Qt5.11.0\5.11.0\msvc2017_64\include\QtGui;D:\Qt\Qt5.11.0\5.11.0\msvc2017_64\include\QtANGLE;D:\Qt\Qt5.11.0\5.11.0\msvc2017_64\include\QtMultimedia;D:\Qt\Qt5.11.0\5.11.0\msvc2017_64\include\QtNetwork;%(AdditionalIncludeDirectories) diff --git a/NineChess/ninechess.vcxproj.filters b/NineChess/ninechess.vcxproj.filters index 5d243ebe..59838707 100644 --- a/NineChess/ninechess.vcxproj.filters +++ b/NineChess/ninechess.vcxproj.filters @@ -84,9 +84,6 @@ Model - - Model - @@ -113,7 +110,10 @@ Control - + + Model + + Model diff --git a/NineChess/src/HashNode.h b/NineChess/src/HashNode.h new file mode 100644 index 00000000..625fa0db --- /dev/null +++ b/NineChess/src/HashNode.h @@ -0,0 +1,155 @@ +#ifndef HASH_NODE_H_ +#define HASH_NODE_H_ + +#include +namespace CTSL //Concurrent Thread Safe Library +{ + // Class representing a templatized hash node + template + class HashNode + { + public: + HashNode() : next(nullptr) + {} + HashNode(K key_, V value_) : next(nullptr), key(key_), value(value_) + {} + ~HashNode() + { + next = nullptr; + } + + const K& getKey() const {return key;} + void setValue(V value_) {value = value_;} + const V& getValue() const {return value;} + + HashNode *next; //Pointer to the next node in the same bucket + private: + K key; //the hash key + V value; //the value corresponding to the key + }; + + + //Class representing a hash bucket. The bucket is implemented as a singly linked list. + //A bucket is always constructed with a dummy head node + template + class HashBucket + { + public: + HashBucket() : head(nullptr) + {} + + ~HashBucket() //delete the bucket + { + clear(); + } + + //Function to find an entry in the bucket matching the key + //If key is found, the corresponding value is copied into the parameter "value" and function returns true. + //If key is not found, function returns false + bool find(const K &key, V &value) const + { + // A shared mutex is used to enable mutiple concurrent reads + std::shared_lock lock(mutex_); + HashNode * node = head; + + while (node != nullptr) + { + if (node->getKey() == key) + { + value = node->getValue(); + return true; + } + node = node->next; + } + return false; + } + + //Function to insert into the bucket + //If key already exists, update the value, else insert a new node in the bucket with the pair + void insert(const K &key, const V &value) + { + //Exclusive lock to enable single write in the bucket + std::unique_lock lock(mutex_); + HashNode * prev = nullptr; + HashNode * node = head; + + while (node != nullptr && node->getKey() != key) + { + prev = node; + node = node->next; + } + + if (nullptr == node) //New entry, create a node and add to bucket + { + if(nullptr == head) + { + head = new HashNode(key, value); + } + else + { + prev->next = new HashNode(key, value); + } + } + else + { + node->setValue(value); //Key found in bucket, update the value + } + + } + + //Function to remove an entry from the bucket, if found + void erase(const K &key) + { + //Exclusive lock to enable single write in the bucket + std::unique_lock lock(mutex_); + HashNode *prev = nullptr; + HashNode * node = head; + + while (node != nullptr && node->getKey() != key) + { + prev = node; + node = node->next; + } + + if (nullptr == node) //Key not found, nothing to be done + { + return; + } + else //Remove the node from the bucket + { + if(head == node) + { + head = node->next; + } + else + { + prev->next = node->next; + } + delete node; //Free up the memory + } + } + + //Function to clear the bucket + void clear() + { + //Exclusive lock to enable single write in the bucket + std::unique_lock lock(mutex_); + HashNode * prev = nullptr; + HashNode * node = head; + while(node != nullptr) + { + prev = node; + node = node->next; + delete prev; + } + head = nullptr; + } + + private: + HashNode * head; //The head node of the bucket + mutable std::shared_timed_mutex mutex_; //The mutex for this bucket + }; +} + +#endif + diff --git a/NineChess/src/aithread.cpp b/NineChess/src/aithread.cpp index 618ad2e9..9df0ced2 100644 --- a/NineChess/src/aithread.cpp +++ b/NineChess/src/aithread.cpp @@ -35,7 +35,7 @@ void AiThread::setAi(const NineChess &chess) ai_ab.setChess(*(this->chess_)); #ifdef HASH_MAP_ENABLE - ai_ab.clearHashMap(); + //ai_ab.clearHashMap(); #endif mutex.unlock(); diff --git a/NineChess/src/hashmap.cpp b/NineChess/src/hashmap.cpp index 9e6bcc28..54156500 100644 --- a/NineChess/src/hashmap.cpp +++ b/NineChess/src/hashmap.cpp @@ -79,6 +79,7 @@ void HashBucket::erase(const K &key) } } +#if 0 // Function to clear the bucket template void HashBucket::clear() @@ -94,6 +95,7 @@ void HashBucket::clear() } head = nullptr; } +#endif //////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/NineChess/src/hashmap.h b/NineChess/src/hashmap.h index d264ad73..e59bc4e5 100644 --- a/NineChess/src/hashmap.h +++ b/NineChess/src/hashmap.h @@ -1,134 +1,80 @@ -#ifndef HASH_MAP_H_ -#define HASH_MAP_H_ - -#include -#include -#include -#include -#include - -constexpr size_t HASH_SIZE_DEFAULT = 1031; // A prime number as hash size gives a better distribution of values in buckets - -namespace CTSL //Concurrent Thread Safe Library -{ -// Class representing a templatized hash node -template -class HashNode -{ -public: - HashNode() : next(nullptr) - { - } - HashNode(K key_, V value_) : next(nullptr), key(key_), value(value_) - { - } - ~HashNode() - { - next = nullptr; - } - - const K &getKey() const - { - return key; - } - void setValue(V value_) - { - value = value_; - } - const V &getValue() const - { - return value; - } - - HashNode *next; // Pointer to the next node in the same bucket -private: - K key; // the hash key - V value; // the value corresponding to the key -}; - - -// Class representing a hash bucket. The bucket is implemented as a singly linked list. -// A bucket is always constructed with a dummy head node -template -class HashBucket -{ -public: - HashBucket() : head(nullptr) - { - } - - ~HashBucket() //delete the bucket - { - clear(); - } - - // Function to find an entry in the bucket matching the key - // If key is found, the corresponding value is copied into the parameter "value" and function returns true. - // If key is not found, function returns false - bool find(const K &key, V &value) const; - - // Function to insert into the bucket - // If key already exists, update the value, else insert a new node in the bucket with the pair - void insert(const K &key, const V &value); - - // Function to remove an entry from the bucket, if found - void erase(const K &key); - - // Function to clear the bucket - void clear(); - -private: - HashNode *head; //The head node of the bucket - mutable std::shared_timed_mutex mutex_; //The mutex for this bucket -}; - -// The class represting the hash map. -// It is expected for user defined types, the hash function will be provided. -// By default, the std::hash function will be used -// If the hash size is not provided, then a defult size of 1031 will be used -// The hash table itself consists of an array of hash buckets. -// Each hash bucket is implemented as singly linked list with the head as a dummy node created -// during the creation of the bucket. All the hash buckets are created during the construction of the map. -// Locks are taken per bucket, hence multiple threads can write simultaneously in different buckets in the hash map -template > -class HashMap -{ -public: - HashMap(size_t hashSize_ = HASH_SIZE_DEFAULT) : hashSize(hashSize_) - { - hashTable = new HashBucket[hashSize]; // create the hash table as an array of hash buckets - } - - ~HashMap() - { - delete[] hashTable; - } - - // Copy and Move of the HashMap are not supported at this moment - HashMap(const HashMap &) = delete; - HashMap(HashMap &&) = delete; - HashMap &operator=(const HashMap &) = delete; - HashMap &operator=(HashMap &&) = delete; - - // Function to find an entry in the hash map matching the key. - // If key is found, the corresponding value is copied into the parameter "value" and function returns true. - // If key is not found, function returns false. - bool find(const K &key, V &value) const; - - // Function to insert into the hash map. - // If key already exists, update the value, else insert a new node in the bucket with the pair. - void insert(const K &key, const V &value); - - // Function to remove an entry from the bucket, if found - void erase(const K &key); - - // Function to clean up the hasp map, i.e., remove all entries from it - void clear(); - -private: - HashBucket *hashTable; - F hashFn; - const size_t hashSize; -}; -} -#endif /* HASH_MAP_H_ */ +#ifndef HASH_MAP_H_ +#define HASH_MAP_H_ + +#include +#include +#include +#include +#include "HashNode.h" + +constexpr size_t HASH_SIZE_DEFAULT = 1031; // A prime number as hash size gives a better distribution of values in buckets +namespace CTSL //Concurrent Thread Safe Library +{ + //The class represting the hash map. + //It is expected for user defined types, the hash function will be provided. + //By default, the std::hash function will be used + //If the hash size is not provided, then a defult size of 1031 will be used + //The hash table itself consists of an array of hash buckets. + //Each hash bucket is implemented as singly linked list with the head as a dummy node created + //during the creation of the bucket. All the hash buckets are created during the construction of the map. + //Locks are taken per bucket, hence multiple threads can write simultaneously in different buckets in the hash map + template > + class HashMap + { + public: + HashMap(size_t hashSize_ = HASH_SIZE_DEFAULT) : hashSize(hashSize_) + { + hashTable = new HashBucket[hashSize]; //create the hash table as an array of hash buckets + } + + ~HashMap() + { + delete [] hashTable; + } + //Copy and Move of the HashMap are not supported at this moment + HashMap(const HashMap&) = delete; + HashMap(HashMap&&) = delete; + HashMap& operator=(const HashMap&) = delete; + HashMap& operator=(HashMap&&) = delete; + + //Function to find an entry in the hash map matching the key. + //If key is found, the corresponding value is copied into the parameter "value" and function returns true. + //If key is not found, function returns false. + bool find(const K &key, V &value) const + { + size_t hashValue = hashFn(key) % hashSize ; + return hashTable[hashValue].find(key, value); + } + + //Function to insert into the hash map. + //If key already exists, update the value, else insert a new node in the bucket with the pair. + void insert(const K &key, const V &value) + { + size_t hashValue = hashFn(key) % hashSize ; + hashTable[hashValue].insert(key, value); + } + + //Function to remove an entry from the bucket, if found + void erase(const K &key) + { + size_t hashValue = hashFn(key) % hashSize ; + hashTable[hashValue].erase(key); + } + + //Function to clean up the hasp map, i.e., remove all entries from it + void clear() + { + for(size_t i = 0; i < hashSize; i++) + { + (hashTable[i]).clear(); + } + } + + private: + HashBucket * hashTable; + F hashFn; + const size_t hashSize; + }; +} +#endif + diff --git a/NineChess/src/ninechess.cpp b/NineChess/src/ninechess.cpp index 80bc33ea..46ea56dd 100644 --- a/NineChess/src/ninechess.cpp +++ b/NineChess/src/ninechess.cpp @@ -183,7 +183,6 @@ NineChess::~NineChess() void NineChess::constructHash() { context.hash = 0ull; - context.hashCheckCode = 0ull; context.gameMovingHash = rand64(); context.actionCaptureHash = rand64(); @@ -397,7 +396,6 @@ bool NineChess::setContext(const struct Rule *rule, int maxStepsLedToDraw, int m memset(context.board, 0, sizeof(context.board)); #ifdef HASH_MAP_ENABLE context.hash = 0ull; - context.hashCheckCode = 0ull; #endif } else { memcpy(context.board, board, sizeof(context.board)); @@ -557,9 +555,8 @@ bool NineChess::reset() elapsedMS_1 = elapsedMS_2 = 0; #ifdef HASH_MAP_ENABLE - // 哈希以及哈希校验码归零 + // 哈希归零 context.hash = 0; - context.hashCheckCode = 0; #endif // 提示 @@ -1324,14 +1321,10 @@ uint64_t NineChess::getHash() return context.hash; } -uint64_t NineChess::getHashCheckCode() -{ - return context.hashCheckCode; -} - // hash函数,对应可重复去子的规则 uint64_t NineChess::updateHash(int pos) { +#if 0 /* * hashCheckCode 各数据位详解(并无冲突,是算法用到的棋局数据的完全表示) * 56-63位:空白不用,全为0 @@ -1342,33 +1335,33 @@ uint64_t NineChess::updateHash(int pos) * 4-5位(共2位):待去子数,最大为3,用2个二进制位表示即可 * 0-3位:player1的手棋数,不需要player2的(可计算出) */ +#endif uint64_t hash = 0ull; - // TODO: 本函数效率低下,啥时调用? for (int i = POS_BEGIN; i < POS_END; i++) { // hash ^= context.zobrist[i][pointType]; // TODO: 待完善 } uint64_t temp = board_[pos] & 0x30 >> 4; - context.hashCheckCode |= (temp) << ((pos - 8) * 2 + 6); + //context.hashCheckCode |= (temp) << ((pos - 8) * 2 + 6); // TODO: context.hash = if (context.turn == PLAYER2) { - context.hashCheckCode |= 1ull << 55; + //context.hashCheckCode |= 1ull << 55; context.hash ^= context.player2sTurnHash; } if (context.action == ACTION_CAPTURE) { - context.hashCheckCode |= 1ull << 54; + //context.hashCheckCode |= 1ull << 54; context.hash ^= context.actionCaptureHash; } - context.hashCheckCode |= (uint64_t)context.nPiecesNeedRemove << 4; - context.hashCheckCode |= context.nPiecesInHand_1; + //context.hashCheckCode |= (uint64_t)context.nPiecesNeedRemove << 4; + //context.hashCheckCode |= context.nPiecesInHand_1; // TODO: hash 应该 不需要 - return context.hashCheckCode; // TODO: 返回什么 + return context.hash; // TODO: 返回什么 } #endif /* HASH_MAP_ENABLE */ diff --git a/NineChess/src/ninechess.h b/NineChess/src/ninechess.h index bc845e99..dd37577b 100644 --- a/NineChess/src/ninechess.h +++ b/NineChess/src/ninechess.h @@ -180,15 +180,9 @@ public: int board[N_POINTS]; #ifdef HASH_MAP_ENABLE - // 局面哈希的校验码(过时) - uint64_t hashCheckCode; - // 局面的哈希值 uint64_t hash; - // 哈希表中的地址, 为 hash 的后 16 位 - uint16_t hashAddr; - // 标记处于走子阶段的哈希 uint64_t gameMovingHash; @@ -497,7 +491,6 @@ protected: #ifdef HASH_MAP_ENABLE // hash相关 uint64_t getHash(); - uint64_t getHashCheckCode(); uint64_t updateHash(int pos); #endif /* HASH_MAP_ENABLE */ diff --git a/NineChess/src/ninechessai_ab.cpp b/NineChess/src/ninechessai_ab.cpp index 9fb1a12a..50fe6e67 100644 --- a/NineChess/src/ninechessai_ab.cpp +++ b/NineChess/src/ninechessai_ab.cpp @@ -13,10 +13,12 @@ #include #include "ninechessai_ab.h" -#include "hashmap.h" +#include "hashMap.h" + +using namespace CTSL; #ifdef HASH_MAP_ENABLE -static std::unique_ptr> instance; +HashMap hashmap; #endif NineChessAi_ab::NineChessAi_ab() : @@ -638,9 +640,6 @@ int NineChessAi_ab::alphaBetaPruning(int depth, int alpha, int beta, Node *node) #ifdef HASH_MAP_ENABLE // 检索 hashmap - uint64_t hashCheckCode = chessTemp.getHashCheckCode(); - node->hashCheckCode = hashCheckCode; - uint64_t hash = chessTemp.getHash(); node->hash = hash; @@ -856,12 +855,12 @@ int NineChessAi_ab::alphaBetaPruning(int depth, int alpha, int beta, Node *node) } #ifdef HASH_MAP_ENABLE -int NineChessAi_ab::recordHash(HashValue &hashValue) +int NineChessAi_ab::recordHash(const HashValue &hashValue) { #ifdef HASH_MAP_ENABLE //hashMapMutex.lock(); //HashMap::insert(hashValue.hash, hashValue); - + hashmap.insert(hashValue.hash, hashValue); //hashMapMutex.unlock(); #endif // HASH_MAP_ENABLE diff --git a/NineChess/src/ninechessai_ab.h b/NineChess/src/ninechessai_ab.h index 7f61d22d..46773fff 100644 --- a/NineChess/src/ninechessai_ab.h +++ b/NineChess/src/ninechessai_ab.h @@ -15,9 +15,10 @@ #include #include "ninechess.h" -#include "hashmap.h" +#include "hashMap.h" using namespace std; +using namespace CTSL; // 注意:NineChess类不是线程安全的! // 所以不能在ai类中修改NineChess类的静态成员变量,切记! @@ -61,7 +62,6 @@ public: int rand; // 随机数,对于 value 一致的结点随机排序用 #ifdef HASH_MAP_ENABLE uint64_t hash; - uint64_t hashCheckCode; bool isHash; // 是否从 Hash 读取 #endif /* HASH_MAP_ENABLE */ bool pruned; // 是否在此处剪枝 @@ -145,11 +145,6 @@ protected: // 增加新节点 struct Node *addNode(Node *parent, int value, NineChess::move_t move, enum NineChess::Player player); -#ifdef HASH_MAP_ENABLE - // 插入哈希表 - int recordHash(HashValue &hashValue); -#endif - // 评价函数 int evaluate(Node *node); @@ -172,6 +167,9 @@ protected: #ifdef HASH_MAP_ENABLE // 查找哈希表 HashValue findHash(uint64_t hash); + + // 插入哈希表 + int recordHash(const HashValue &hashValue); #endif private: @@ -219,7 +217,7 @@ private: char cmdline[32]; #ifdef HASH_MAP_ENABLE - HashMap hashmap; + //HashMap hashmap; #endif };