/// <summary> /// 输出开局库中元素的散列分布图,用图示的办法来查看散列的效果 /// 2048行、2048列,2^11 * 2^11 * 4 = 16M 正好是16M个盘面情况 /// 每4个元素为一组,未占用是白色,占用1个是黄色,占用2个是绿色,全部占用(3个)是蓝色 /// 通过输出的位图可以看到散列效果还是不错的 /// </summary> //public void SaveHashPicture(string filename) //{ // using (System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(2048, 2048, System.Drawing.Imaging.PixelFormat.Format24bppRgb)) // { // fs.Seek(0, SeekOrigin.Begin); // for (int h = 0; h < 2048; h++) // for (int w = 0; w < 2048; w++) // { // int count = 0; // for (int i = 0; i < 4; i++) // { // //从文件中取出6个字节,前4个字节是zobrist值,后2字节是相同盘面统计数 // byte[] bytes6 = new byte[6]; // fs.Read(bytes6, 0, 6); // int zobristInOpening = BitConverter.ToInt32(bytes6, 0); //前4字节 // //countSameBoard = BitConverter.ToUInt16(bytes6, 4); //后2个字节 // if (zobristInOpening != 0) // { // ++count; // } // } // if (count == 0) // bmp.SetPixel(w, h, System.Drawing.Color.White); // else if (count == 1) // bmp.SetPixel(w, h, System.Drawing.Color.Yellow); // else if (count == 2) // bmp.SetPixel(w, h, System.Drawing.Color.Green); // else // bmp.SetPixel(w, h, System.Drawing.Color.Blue); // } // bmp.Save(filename, System.Drawing.Imaging.ImageFormat.Png); // } //} public Move FindBestMove(Board board) { Move maxUsedMove = new Move(0, 0, 0, 0); // 使用率最高的着法 int maxCount = 0; // 保存使用得最多的次数 // 在所有的可行的着法里查找一遍,可以发现这种办法效率有点低 Move[] movelist = new Move[200]; MoveGenerator.GenAllMoveList(board, movelist); foreach (Move m in movelist) { UInt16 countSameBoard; board.MakeMove(m); FindBoardHash(Zobrist.ZoristHash(board), out countSameBoard); //if (countSameBoard != 0) // Console.WriteLine(m + ": " + countSameBoard); if (countSameBoard > maxCount) { maxCount = countSameBoard; maxUsedMove = new Move(m); } board.UnmakeMove(m); // 要恢复原始的盘面 } return(maxUsedMove); }
///// <summary> ///// 使用了置换表的AlphaBeta剪枝搜索 ///// </summary> ///// <param name="depth">当前搜索的深度,0代表树根</param> ///// <param name="maxDepth">最大搜索深度</param> ///// <param name="alpha">初始值可以设置为MIN_EVAL_VALUE</param> ///// <param name="beta">初始值可以设置为MAX_EVAL_VALUE</param> ///// <returns></returns> //public int NegaAlphaBetaTT_old(int depth, int maxDepth, int alpha, int beta) //{ // //if (depth == 0) bestMove = new Move(0,0,0,0); // ulong boardHash = Zobrist.ZoristHash(board); // // 探查置换表 // NodeOfSearch entry = transpositionTable[boardHash]; //ProbeHash(boardHash); // // (depth=0) a // // / \ // // (depth=1) b c 这里的b是已经搜索过的节点,已经保存在置换表中,entry.Depth = 1 // // / \ \ // // (depth=2) d e f (当前搜索的节点是f) depth = 2 // // .................. // // 假设b是以前搜索过的节点,已经保存在置换表中,entry.depth=1 // // 当前搜索的节点是f,depth=2,如果f与b的局面相同,由于b的评估值是经过了更深层的搜索得到的, // // 所以f可以直接用b的评估值,这个结果只会好,不会差,所以应该判断entry.Depth <= depth // if (entry != null && (entry.MaxDepth - entry.Depth) >= (maxDepth - depth) ) // //if (entry != null && entry.Depth <= depth) // { // //PrintDebugInfo("Hash表命中!" + entry); // if (entry.Type == NodeOfSearch.PV_NODES) // { // return entry.Score; // } // if (entry.Type == NodeOfSearch.ALL_NODES && entry.Score <= alpha) // { // //PrintDebugInfo("Hash表命中!修改alpha:" + entry.Score + " -> " + alpha); // alpha = entry.Score; // //return alpha; // } // if (entry.Type == NodeOfSearch.CUT_NODES && entry.Score >= beta) // { // //PrintDebugInfo("Hash表命中!修改beta:" + entry.Score + " -> " + beta); // beta = entry.Score; // //return beta; // } // //if (alpha >= beta) // //{ // // PrintDebugInfo("***************这里还会发生alpha>=beta?"); // // //return entry.Score; // //} // } // // 到达叶子节点,搜索到最大深度了 // if (depth == maxDepth) // { // int valueLeaf = Evaluator.EvaluateAllWithSide(board); // // 应该肯定是EXACT节点吧? // /* if(v0 <= alpha) // node.Type = NODE_ALPHA; // else if(v0 >= beta) // node.Type = NODE_BETA; // else */ // NodeOfSearch nodeLeaf = new NodeOfSearch(boardHash, depth, maxDepth, NodeOfSearch.PV_NODES, valueLeaf); // transpositionTable.RecordHash(nodeLeaf); // //DEBUG(DBG_DEBUG, SPACES[depth] << "到达最大深度:" << " return score: "<< v0); // return valueLeaf; // } // int nodeType = NodeOfSearch.ALL_NODES; // Move[] moveList = new Move[200]; // int countMove = MoveGenerator.GenAllMoveList(board, moveList); // // 无着可走,说明是终止局面,即被将死 // if (countMove == 0) // { // int scoreEndStatus = Evaluator.MIN_EVAL_VALUE + depth; // NodeOfSearch nodeEnd = new NodeOfSearch(boardHash, depth, maxDepth, NodeOfSearch.PV_NODES, scoreEndStatus); // transpositionTable.RecordHash(nodeEnd); // return scoreEndStatus; // } // // 利用了置换表中的历史评估数据,进行着法排序 // // 局面"9/4a4/3k5/3N5/3N5/r8/9/9/9/4K4 w" // // 用迭代加深算法来测试效果:迭代加深计算到第8层 // // DEBUG // // 不排序时1.7秒,探查并对着法排序时:17秒,代价很大 // // Release // // 不排序时0.7秒,探查并对着法排序时:7秒 // // if(depth == 0) // // SortMovelist(moveList, countMove); // //int bestScore = Evaluator.MIN_EVAL_VALUE + depth; // for (int i = 0; i < countMove; i++) // { // board.MakeMove(moveList[i]); // int score = -NegaAlphaBetaTT_old(depth + 1, maxDepth, -beta, -alpha); // board.UnmakeMove(moveList[i]); // // 这里负责记录最佳着法 // //if (score > bestScore) // //{ // // bestScore = score; // // if(depth == 0) bestMove = moveList[i]; // //} // if (score >= beta) // { // //PrintDebugInfo("发生剪枝!bestScore >= beta: " + bestScore + " >= " + beta); // // 应该记录该节点!! // NodeOfSearch nodeBeta = new NodeOfSearch(boardHash, depth, maxDepth, NodeOfSearch.CUT_NODES, beta); // transpositionTable.RecordHash(nodeBeta); // return beta;// score; // } // if (score > alpha) // { // // alpha = bestScore; // alpha = score???? // // 这时只是记录alpha值的变化情况,并不写置换表 // nodeType = NodeOfSearch.PV_NODES; // alpha = score; // //PrintDebugInfo("修改alpha: " + alpha); // } // } // //int type = // // (bestScore <= alpha) ? NodeOfSearch.NODE_TYPE_ALPHA // // : (bestScore >= beta) ? NodeOfSearch.NODE_TYPE_BETA // // : NodeOfSearch.NODE_TYPE_EXACT; // //NodeOfSearch node = new NodeOfSearch(boardHash, depth, maxDepth, type, bestScore); // NodeOfSearch node = new NodeOfSearch(boardHash, depth, maxDepth, nodeType, alpha); // transpositionTable.RecordHash(node); // //return bestScore; // return alpha; //} /// <summary> /// 试验数据,对于局面"9/4a4/3k5/3N5/3N5/r8/9/9/9/4K4 w" // 用迭代加深算法来测试效果:迭代加深计算到第8层 // DEBUG // 不排序时1.7秒,探查并对着法排序时:17秒,代价很大 // Release // 不排序时0.7秒,探查并对着法排序时:7秒 /// </summary> /// <param name="moveList"></param> /// <param name="countMove"></param> private void SortMovelist(Move[] moveList, int countMove) { for (int i = 0; i < countMove; i++) { board.MakeMove(moveList[i]); ulong boardHash = Zobrist.ZoristHash(board); // 探查置换表 //NodeOfSearch entry = transpositionTable.Probe(boardHash); //if (entry != null) //{ // moveList[i].Score = entry.Score; //} //else // moveList[i].Score = 0; board.UnmakeMove(moveList[i]); } Array.Sort(moveList, 0, countMove); }
/// <summary> /// 把一盘PGN对局里的前N个着法放入对局库中 /// </summary> /// <param name="pgnFilename">PGN文件名</param> /// <returns>加入到开局库时,返回true /// 如果在解析PGN时遇到不规范的棋谱时,返回false,此时控制台会打印出错误信息</returns> public static bool AddPgnFileToOpeningBook(string bookFilename, string pgnFilename, int maxStep) { using (OpeningBook book = new OpeningBook(bookFilename)) { // 从PGN文件里读出所有着法来,这里用的是纵列格式 string[] allmoves = PgnUtil.GetAllMovesFromPgnFile(pgnFilename); Board board = new Board(); int numMove = 0; // 记录已走了第几步了 foreach (string strMove in allmoves) { if (numMove >= maxStep) { break; } try { // 走一步后,把盘面生成zobrist值,保存到开局库里 // TODO: board.CreateMoveFromString(NotationConverter.Convert(board, strMove)); Move move = MoveNotation.CreateMoveFromChineseNotation(board, strMove); board.MakeMove(move); // Console.WriteLine("==== " + strMove + "\n" + board); ++numMove; ulong zobrist = Zobrist.ZoristHash(board); book.InsertBoardZobrist(zobrist); } catch (Exception e) { Console.WriteLine("--- " + strMove + " ---"); Console.WriteLine(e.Message); Console.WriteLine(board); return(false); } } // end 对每一着法循环结束 } return(true); }