00001 //---------------------------------------------------------------------------- 00002 /** @file BenzeneHtpEngine.cpp 00003 */ 00004 //---------------------------------------------------------------------------- 00005 00006 #include "SgSystem.h" 00007 #include "SgGameReader.h" 00008 00009 #include <cmath> 00010 #include <functional> 00011 #include "BoardUtils.hpp" 00012 #include "BitsetIterator.hpp" 00013 #include "GraphUtils.hpp" 00014 #include "HexProgram.hpp" 00015 #include "HexSgUtil.hpp" 00016 #include "BenzeneHtpEngine.hpp" 00017 #include "Resistance.hpp" 00018 #include "DfsSolver.hpp" 00019 #include "SwapCheck.hpp" 00020 #include "TwoDistance.hpp" 00021 #include "VCSet.hpp" 00022 #include "VCUtils.hpp" 00023 00024 using namespace benzene; 00025 00026 //---------------------------------------------------------------------------- 00027 00028 BenzeneHtpEngine::BenzeneHtpEngine(int boardsize) 00029 : HexHtpEngine(boardsize), 00030 m_pe(m_board.Width(), m_board.Height()), 00031 m_se(m_board.Width(), m_board.Height()), 00032 m_dfsSolver(), 00033 m_dfpnSolver(), 00034 m_dfsHashTable(new DfsHashTable(20)), // TT with 2^20 entries 00035 m_dfpnHashTable(new DfpnHashTable(21)), // TT with 2^21 entries 00036 m_dfsDB(0), 00037 m_dfpnDB(0), 00038 m_dfsParam(), 00039 m_dfpnParam(), 00040 m_dfsPositions(m_dfsHashTable, m_dfsDB, m_dfsParam), 00041 m_dfpnPositions(m_dfpnHashTable, m_dfpnDB, m_dfpnParam), 00042 m_playerEnvCommands(m_pe), 00043 m_solverEnvCommands(m_se), 00044 m_vcCommands(m_game, m_pe), 00045 m_dfsSolverCommands(m_game, m_se, m_dfsSolver, m_dfsHashTable, m_dfsDB, 00046 m_dfsPositions), 00047 m_dfpnSolverCommands(m_game, m_se, m_dfpnSolver, m_dfpnHashTable, 00048 m_dfpnDB, m_dfpnPositions), 00049 m_useParallelSolver(false) 00050 { 00051 RegisterCmd("benzene-license", &BenzeneHtpEngine::CmdLicense); 00052 00053 RegisterCmd("get_absorb_group", &BenzeneHtpEngine::CmdGetAbsorbGroup); 00054 00055 RegisterCmd("handbook-add", &BenzeneHtpEngine::CmdHandbookAdd); 00056 00057 RegisterCmd("compute-inferior", &BenzeneHtpEngine::CmdComputeInferior); 00058 RegisterCmd("compute-fillin", &BenzeneHtpEngine::CmdComputeFillin); 00059 RegisterCmd("compute-vulnerable", &BenzeneHtpEngine::CmdComputeVulnerable); 00060 RegisterCmd("compute-reversible", &BenzeneHtpEngine::CmdComputeReversible); 00061 RegisterCmd("compute-dominated", &BenzeneHtpEngine::CmdComputeDominated); 00062 RegisterCmd("compute-dominated-cell", 00063 &BenzeneHtpEngine::CmdComputeDominatedOnCell); 00064 RegisterCmd("find-comb-decomp", &BenzeneHtpEngine::CmdFindCombDecomp); 00065 RegisterCmd("find-split-decomp", &BenzeneHtpEngine::CmdFindSplitDecomp); 00066 RegisterCmd("encode-pattern", &BenzeneHtpEngine::CmdEncodePattern); 00067 00068 m_playerEnvCommands.Register(*this, "player"); 00069 m_solverEnvCommands.Register(*this, "solver"); 00070 m_vcCommands.Register(*this); 00071 m_dfsSolverCommands.Register(*this); 00072 m_dfpnSolverCommands.Register(*this); 00073 00074 RegisterCmd("eval-twod", &BenzeneHtpEngine::CmdEvalTwoDist); 00075 RegisterCmd("eval-resist", &BenzeneHtpEngine::CmdEvalResist); 00076 RegisterCmd("eval-resist-delta", &BenzeneHtpEngine::CmdEvalResistDelta); 00077 RegisterCmd("eval-influence", &BenzeneHtpEngine::CmdEvalInfluence); 00078 00079 RegisterCmd("misc-debug", &BenzeneHtpEngine::CmdMiscDebug); 00080 } 00081 00082 BenzeneHtpEngine::~BenzeneHtpEngine() 00083 { 00084 } 00085 00086 //---------------------------------------------------------------------------- 00087 00088 void BenzeneHtpEngine::RegisterCmd(const std::string& name, 00089 GtpCallback<BenzeneHtpEngine>::Method method) 00090 { 00091 Register(name, new GtpCallback<BenzeneHtpEngine>(this, method)); 00092 } 00093 00094 void BenzeneHtpEngine::NewGame(int width, int height) 00095 { 00096 HexHtpEngine::NewGame(width, height); 00097 00098 m_pe.NewGame(width, height); 00099 m_se.NewGame(width, height); 00100 } 00101 00102 //////////////////////////////////////////////////////////////////////// 00103 // Commands 00104 //////////////////////////////////////////////////////////////////////// 00105 00106 /** Displays usage license. */ 00107 void BenzeneHtpEngine::CmdLicense(HtpCommand& cmd) 00108 { 00109 cmd << 00110 HexProgram::Get().getName() << " " << 00111 HexProgram::Get().getVersion() << " " << 00112 HexProgram::Get().getDate() << "\n" 00113 "Copyright (C) 2010 by the authors of the Benzene project.\n" 00114 "See http://benzene.sourceforge.net for information about benzene.\n" 00115 "Benzene comes with NO WARRANTY to the extent permitted by law.\n" 00116 "This program is free software; you can redistribute it and/or\n" 00117 "modify it under the terms of the GNU Lesser General Public License\n" 00118 "as published by the Free Software Foundation - version 3. For more\n" 00119 "information about these matters, see the files COPYING and COPYING.LESSER.\n"; 00120 } 00121 00122 /** Returns the set of stones this stone is part of. */ 00123 void BenzeneHtpEngine::CmdGetAbsorbGroup(HtpCommand& cmd) 00124 { 00125 cmd.CheckNuArg(1); 00126 HexPoint cell = HtpUtil::MoveArg(cmd, 0); 00127 if (m_game.Board().GetColor(cell) == EMPTY) 00128 return; 00129 00130 Groups groups; 00131 GroupBuilder::Build(m_game.Board(), groups); 00132 00133 const Group& group = groups.GetGroup(cell); 00134 cmd << group.Captain(); 00135 for (BitsetIterator p(group.Members()); p; ++p) 00136 if (*p != group.Captain()) 00137 cmd << ' ' << *p; 00138 } 00139 00140 //---------------------------------------------------------------------------- 00141 00142 /** Pulls moves out of the game for given color and appends them to 00143 the given handbook file. Skips the first move (ie, the move from 00144 the empty board). Performs no duplicate checking. 00145 Usage: 00146 handbook-add [handbook.txt] [sgf file] [color] [max move #] 00147 */ 00148 void BenzeneHtpEngine::CmdHandbookAdd(HtpCommand& cmd) 00149 { 00150 cmd.CheckNuArg(4); 00151 std::string bookfilename = cmd.Arg(0); 00152 std::string sgffilename = cmd.Arg(1); 00153 HexColor colorToSave = HtpUtil::ColorArg(cmd, 2); 00154 int maxMove = cmd.IntArg(3, 0); 00155 00156 std::ifstream sgffile(sgffilename.c_str()); 00157 if (!sgffile) 00158 throw HtpFailure() << "cannot load sgf"; 00159 00160 SgGameReader sgreader(sgffile, 11); 00161 SgNode* root = sgreader.ReadGame(); 00162 if (root == 0) 00163 throw HtpFailure() << "cannot load file"; 00164 sgreader.PrintWarnings(std::cerr); 00165 00166 if (HexSgUtil::NodeHasSetupInfo(root)) 00167 throw HtpFailure() << "Root has setup info!"; 00168 00169 int size = root->GetIntProp(SG_PROP_SIZE); 00170 if (size != m_game.Board().Width() || 00171 size != m_game.Board().Height()) 00172 throw HtpFailure() << "Sgf boardsize does not match board"; 00173 00174 StoneBoard brd(m_game.Board()); 00175 HexColor color = FIRST_TO_PLAY; 00176 PointSequence responses; 00177 std::vector<hash_t> hashes; 00178 SgNode* cur = root; 00179 for (int moveNum = 0; moveNum < maxMove;) 00180 { 00181 cur = cur->NodeInDirection(SgNode::NEXT); 00182 if (!cur) 00183 break; 00184 00185 if (HexSgUtil::NodeHasSetupInfo(cur)) 00186 throw HtpFailure() << "Node has setup info"; 00187 00188 // SgGameReader does not support reading "resign" moves from 00189 // an sgf, so any such node will have no move. This should not 00190 // be treated as an error if it is the last node in the game. 00191 // This isn't exact, but close enough. 00192 if (!cur->HasNodeMove() && !cur->HasSon()) 00193 break; 00194 00195 // If node does not have a move and is *not* the last node in 00196 // the game, then this sgf should not be passed in here. 00197 if (!cur->HasNodeMove()) 00198 throw HtpFailure() << "Node has no move"; 00199 00200 HexColor sgfColor = HexSgUtil::SgColorToHexColor(cur->NodePlayer()); 00201 HexPoint sgfPoint = HexSgUtil::SgPointToHexPoint(cur->NodeMove(), 00202 brd.Height()); 00203 if (color != sgfColor) 00204 throw HtpFailure() << "Unexpected color to move"; 00205 00206 if (moveNum && color == colorToSave) 00207 { 00208 hashes.push_back(brd.Hash()); 00209 responses.push_back(sgfPoint); 00210 } 00211 brd.PlayMove(color, sgfPoint); 00212 color = !color; 00213 ++moveNum; 00214 } 00215 HexAssert(hashes.size() == responses.size()); 00216 00217 std::ofstream out(bookfilename.c_str(), std::ios_base::app); 00218 for (std::size_t i = 0 ; i < hashes.size(); ++i) 00219 out << HashUtil::toString(hashes[i]) << ' ' << responses[i] << '\n'; 00220 out.close(); 00221 } 00222 00223 //---------------------------------------------------------------------- 00224 00225 /** Outputs inferior cell info for current state. 00226 Usage: "compute-inferior [color]" 00227 */ 00228 void BenzeneHtpEngine::CmdComputeInferior(HtpCommand& cmd) 00229 { 00230 cmd.CheckNuArg(1); 00231 HexColor color = HtpUtil::ColorArg(cmd, 0); 00232 HexBoard& brd = m_pe.SyncBoard(m_game.Board()); 00233 brd.GetPatternState().Update(); 00234 GroupBuilder::Build(brd.GetPosition(), brd.GetGroups()); 00235 InferiorCells inf; 00236 m_pe.ice.ComputeInferiorCells(color, brd.GetGroups(), 00237 brd.GetPatternState(), inf); 00238 cmd << inf.GuiOutput(); 00239 cmd << '\n'; 00240 } 00241 00242 /** Computes fillin for the given board. Color argument affects order 00243 for computing vulnerable/presimplicial pairs. */ 00244 void BenzeneHtpEngine::CmdComputeFillin(HtpCommand& cmd) 00245 { 00246 cmd.CheckNuArg(1); 00247 HexColor color = HtpUtil::ColorArg(cmd, 0); 00248 HexBoard& brd = m_pe.SyncBoard(m_game.Board()); 00249 brd.GetPatternState().Update(); 00250 GroupBuilder::Build(brd.GetPosition(), brd.GetGroups()); 00251 InferiorCells inf; 00252 m_pe.ice.ComputeFillin(color, brd.GetGroups(), brd.GetPatternState(), inf); 00253 inf.ClearVulnerable(); 00254 cmd << inf.GuiOutput(); 00255 cmd << '\n'; 00256 } 00257 00258 /** Computes vulnerable cells on the current board for the given color. */ 00259 void BenzeneHtpEngine::CmdComputeVulnerable(HtpCommand& cmd) 00260 { 00261 cmd.CheckNuArg(1); 00262 HexColor col = HtpUtil::ColorArg(cmd, 0); 00263 HexBoard& brd = m_pe.SyncBoard(m_game.Board()); 00264 brd.GetPatternState().Update(); 00265 GroupBuilder::Build(brd.GetPosition(), brd.GetGroups()); 00266 InferiorCells inf; 00267 m_pe.ice.FindVulnerable(brd.GetPatternState(), col, 00268 brd.GetPosition().GetEmpty(), inf); 00269 cmd << inf.GuiOutput(); 00270 cmd << '\n'; 00271 } 00272 00273 /** Computes reversible cells on the current board for the given color. */ 00274 void BenzeneHtpEngine::CmdComputeReversible(HtpCommand& cmd) 00275 { 00276 cmd.CheckNuArg(1); 00277 HexColor col = HtpUtil::ColorArg(cmd, 0); 00278 HexBoard& brd = m_pe.SyncBoard(m_game.Board()); 00279 brd.GetPatternState().Update(); 00280 GroupBuilder::Build(brd.GetPosition(), brd.GetGroups()); 00281 InferiorCells inf; 00282 m_pe.ice.FindReversible(brd.GetPatternState(), col, 00283 brd.GetPosition().GetEmpty(), inf); 00284 cmd << inf.GuiOutput(); 00285 cmd << '\n'; 00286 } 00287 00288 /** Computes dominated cells on the current board for the given color. */ 00289 void BenzeneHtpEngine::CmdComputeDominated(HtpCommand& cmd) 00290 { 00291 cmd.CheckNuArg(1); 00292 HexColor col = HtpUtil::ColorArg(cmd, 0); 00293 HexBoard& brd = m_pe.SyncBoard(m_game.Board()); 00294 brd.GetPatternState().Update(); 00295 GroupBuilder::Build(brd.GetPosition(), brd.GetGroups()); 00296 InferiorCells inf; 00297 m_pe.ice.FindDominated(brd.GetPatternState(), col, 00298 brd.GetPosition().GetEmpty(), inf); 00299 cmd << inf.GuiOutput(); 00300 cmd << '\n'; 00301 } 00302 00303 void BenzeneHtpEngine::CmdComputeDominatedOnCell(HtpCommand& cmd) 00304 { 00305 cmd.CheckNuArg(2); 00306 HexColor col = HtpUtil::ColorArg(cmd, 0); 00307 HexPoint cell = HtpUtil::MoveArg(cmd, 1); 00308 HexBoard& brd = m_pe.SyncBoard(m_game.Board()); 00309 if (m_game.Board().GetColor(cell) != EMPTY) 00310 return; 00311 brd.GetPatternState().Update(); 00312 PatternHits hits; 00313 m_pe.ice.FindDominatedOnCell(brd.GetPatternState(), col, 00314 cell, hits); 00315 for (std::size_t i=0; i<hits.size(); ++i) 00316 cmd << " " << hits[i].pattern()->getName(); 00317 cmd << '\n'; 00318 } 00319 00320 /** Tries to find a combinatorial decomposition of the board state. 00321 Outputs cells in the vc if there is a decomposition. 00322 Usage: 'find-comb-decomp [color]' 00323 */ 00324 void BenzeneHtpEngine::CmdFindCombDecomp(HtpCommand& cmd) 00325 { 00326 cmd.CheckNuArg(1); 00327 HexColor color = HtpUtil::ColorArg(cmd, 0); 00328 HexBoard& brd = m_pe.SyncBoard(m_game.Board()); 00329 // Turn of decomps in the board, then call ComputeAll(). Otherwise 00330 // decomps will be found and filled-in by ComputeAll(). 00331 bool useDecomps = brd.UseDecompositions(); 00332 brd.SetUseDecompositions(false); 00333 brd.ComputeAll(BLACK); 00334 brd.SetUseDecompositions(useDecomps); 00335 bitset_t capturedVC; 00336 if (BoardUtils::FindCombinatorialDecomposition(brd, color, capturedVC)) 00337 cmd << HexPointUtil::ToString(capturedVC); 00338 } 00339 00340 /** Tries to find a group that crowds both opponent edges. Outputs 00341 group that crowds both edges if one exists. 00342 Usage: 'find-split-decomp [color]' 00343 00344 FIXME: Dump inferior cell info as well? It's hard to see what's 00345 actually going on if it is not displayed. 00346 */ 00347 void BenzeneHtpEngine::CmdFindSplitDecomp(HtpCommand& cmd) 00348 { 00349 cmd.CheckNuArg(1); 00350 HexColor color = HtpUtil::ColorArg(cmd, 0); 00351 HexBoard& brd = m_pe.SyncBoard(m_game.Board()); 00352 brd.ComputeAll(BLACK); 00353 HexPoint group; 00354 if (BoardUtils::FindSplittingDecomposition(brd, color, group)) 00355 cmd << group; 00356 } 00357 00358 /** Outputs pattern in encoded form. 00359 Takes a list of cells, the first cell being the center of the 00360 pattern (that is not actually in the pattern). 00361 FIXME: clean this up! 00362 */ 00363 void BenzeneHtpEngine::CmdEncodePattern(HtpCommand& cmd) 00364 { 00365 HexAssert(cmd.NuArg() > 0); 00366 00367 // Build direction offset look-up matrix. 00368 int xoffset[Pattern::NUM_SLICES][32]; 00369 int yoffset[Pattern::NUM_SLICES][32]; 00370 for (int s=0; s<Pattern::NUM_SLICES; s++) 00371 { 00372 int fwd = s; 00373 int lft = (s + 2) % NUM_DIRECTIONS; 00374 int x1 = HexPointUtil::DeltaX(fwd); 00375 int y1 = HexPointUtil::DeltaY(fwd); 00376 for (int i=1, g=0; i<=Pattern::MAX_EXTENSION; i++) 00377 { 00378 int x2 = x1; 00379 int y2 = y1; 00380 for (int j=0; j<i; j++) 00381 { 00382 xoffset[s][g] = x2; 00383 yoffset[s][g] = y2; 00384 x2 += HexPointUtil::DeltaX(lft); 00385 y2 += HexPointUtil::DeltaY(lft); 00386 g++; 00387 } 00388 x1 += HexPointUtil::DeltaX(fwd); 00389 y1 += HexPointUtil::DeltaY(fwd); 00390 } 00391 } 00392 00393 int pattOut[Pattern::NUM_SLICES * 5]; 00394 memset(pattOut, 0, sizeof(pattOut)); 00395 StoneBoard brd(m_game.Board()); 00396 HexPoint center = HtpUtil::MoveArg(cmd, 0); 00397 LogInfo() << "Center of pattern: " << center << '\n' << "Includes: "; 00398 int x1, y1, x2, y2; 00399 HexPointUtil::pointToCoords(center, x1, y1); 00400 std::size_t i = 1; 00401 while (i < cmd.NuArg()) 00402 { 00403 HexPoint p = HtpUtil::MoveArg(cmd, i++); 00404 HexPointUtil::pointToCoords(p, x2, y2); 00405 x2 = x2 - x1; 00406 y2 = y2 - y1; 00407 int sliceNo; 00408 if (y2 > 0) 00409 { 00410 if ((x2 + y2) < 0) // Point is in bottom of 4th slice 00411 sliceNo = 3; 00412 else if ((x2 < 0)) // Point is in 5th slice 00413 sliceNo = 4; 00414 else // point is in 6th slice 00415 sliceNo = 5; 00416 } 00417 else 00418 { 00419 if ((x2 + y2) > 0) // Point is in 1st slice 00420 sliceNo = 0; 00421 else if (x2 > 0) // Point is in 2nd slice 00422 sliceNo = 1; 00423 else if (x2 < 0 && y2 == 0) // Point is in upper part of 4th slice 00424 sliceNo = 3; 00425 else // Point is in 3rd slice 00426 sliceNo = 2; 00427 } 00428 int j = 0; 00429 while (j < 32 && (xoffset[sliceNo][j] != x2 || 00430 yoffset[sliceNo][j] != y2)) 00431 j++; 00432 HexAssert(j != 32); 00433 pattOut[sliceNo*5] += (1 << j); 00434 00435 if (brd.IsBlack(p)) 00436 pattOut[(sliceNo*5) + 1] += (1 << j); 00437 else if (brd.IsWhite(p)) 00438 pattOut[(sliceNo*5) + 2] += (1 << j); 00439 LogInfo() << p << ":" << brd.GetColor(p) << ", "; 00440 } 00441 LogInfo() << '\n'; 00442 00443 std::string encPattStr = "d:"; 00444 00445 for (int k = 0; k < Pattern::NUM_SLICES; k++) 00446 { 00447 for (int l = 0; l < 4; l++) 00448 { 00449 std::stringstream out; //FIXME: Isn't there a better way?? 00450 out << (pattOut[(k*5) + l]) << ","; 00451 encPattStr.append(out.str()); 00452 } 00453 std::stringstream out; 00454 out << (pattOut[(k*5) + 4]) << ";"; 00455 encPattStr.append(out.str()); 00456 } 00457 LogInfo() << encPattStr << '\n'; 00458 } 00459 00460 //---------------------------------------------------------------------------- 00461 // Evaluation commands 00462 //---------------------------------------------------------------------------- 00463 00464 void BenzeneHtpEngine::CmdEvalTwoDist(HtpCommand& cmd) 00465 { 00466 cmd.CheckNuArg(1); 00467 HexColor color = HtpUtil::ColorArg(cmd, 0); 00468 00469 HexBoard& brd = m_pe.SyncBoard(m_game.Board()); 00470 brd.ComputeAll(color); 00471 TwoDistance twod(TwoDistance::ADJACENT); 00472 twod.Evaluate(brd); 00473 00474 for (BoardIterator it(brd.Const().Interior()); it; ++it) 00475 { 00476 if (brd.GetPosition().IsOccupied(*it)) continue; 00477 HexEval energy = twod.Score(*it, color); 00478 if (energy == EVAL_INFINITY) 00479 energy = -1; 00480 cmd << " " << *it << " " << energy; 00481 } 00482 } 00483 00484 void BenzeneHtpEngine::CmdEvalResist(HtpCommand& cmd) 00485 { 00486 cmd.CheckNuArg(1); 00487 HexColor color = HtpUtil::ColorArg(cmd, 0); 00488 00489 HexBoard& brd = *m_pe.brd; 00490 Resistance resist; 00491 resist.Evaluate(brd); 00492 00493 cmd << " res " << std::fixed << std::setprecision(3) << resist.Score() 00494 << " rew " << std::fixed << std::setprecision(3) << resist.Resist(WHITE) 00495 << " reb " << std::fixed << std::setprecision(3) << resist.Resist(BLACK); 00496 00497 for (BoardIterator it(brd.Const().Interior()); it; ++it) { 00498 if (brd.GetPosition().IsOccupied(*it)) continue; 00499 HexEval energy = resist.Score(*it, color); 00500 if (energy == EVAL_INFINITY) 00501 energy = -1; 00502 cmd << " " << *it << " " 00503 << std::fixed << std::setprecision(3) << energy; 00504 } 00505 } 00506 00507 void BenzeneHtpEngine::CmdEvalResistDelta(HtpCommand& cmd) 00508 { 00509 cmd.CheckNuArg(1); 00510 HexColor color = HtpUtil::ColorArg(cmd, 0); 00511 00512 HexBoard& brd = m_pe.SyncBoard(m_game.Board()); 00513 brd.ComputeAll(color); 00514 Resistance resist; 00515 resist.Evaluate(brd); 00516 HexEval base = resist.Score(); 00517 00518 cmd << " res " << std::fixed << std::setprecision(3) << base; 00519 for (BitsetIterator it(brd.GetPosition().GetEmpty()); it; ++it) 00520 { 00521 brd.PlayMove(color, *it); 00522 resist.Evaluate(brd); 00523 HexEval cur = resist.Score(); 00524 cmd << " " << *it << " " 00525 << std::fixed << std::setprecision(3) << (cur - base); 00526 brd.UndoMove(); 00527 } 00528 } 00529 00530 void BenzeneHtpEngine::CmdEvalInfluence(HtpCommand& cmd) 00531 { 00532 cmd.CheckNuArg(1); 00533 HexColor color = HtpUtil::ColorArg(cmd, 0); 00534 00535 HexBoard& brd = m_pe.SyncBoard(m_game.Board()); 00536 brd.ComputeAll(color); 00537 00538 // Pre-compute edge adjacencies 00539 const Groups& groups = brd.GetGroups(); 00540 bitset_t northNbs 00541 = VCSetUtil::ConnectedTo(brd.Cons(BLACK), groups, NORTH, VC::FULL); 00542 bitset_t southNbs 00543 = VCSetUtil::ConnectedTo(brd.Cons(BLACK), groups, SOUTH, VC::FULL); 00544 bitset_t eastNbs 00545 = VCSetUtil::ConnectedTo(brd.Cons(WHITE), groups, EAST, VC::FULL); 00546 bitset_t westNbs 00547 = VCSetUtil::ConnectedTo(brd.Cons(WHITE), groups, WEST, VC::FULL); 00548 00549 for (BoardIterator it(brd.Const().Interior()); it; ++it) { 00550 if (brd.GetPosition().IsOccupied(*it)) continue; 00551 00552 // Compute neighbours, giving over-estimation to edges 00553 bitset_t b1 = VCSetUtil::ConnectedTo(brd.Cons(BLACK), brd.GetGroups(), 00554 *it, VC::FULL); 00555 if (b1.test(NORTH)) b1 |= northNbs; 00556 if (b1.test(SOUTH)) b1 |= southNbs; 00557 b1 &= brd.GetPosition().GetEmpty(); 00558 bitset_t b2 = VCSetUtil::ConnectedTo(brd.Cons(WHITE), brd.GetGroups(), 00559 *it, VC::FULL); 00560 if (b2.test(EAST)) b2 |= eastNbs; 00561 if (b2.test(WEST)) b2 |= westNbs; 00562 b2 &= brd.GetPosition().GetEmpty(); 00563 00564 // Compute ratio of VCs at this cell, and use as measure of influence 00565 double v1 = (double) b1.count(); 00566 double v2 = (double) b2.count(); 00567 HexAssert(v1+v2 >= 1.0); 00568 double influence; 00569 if (color == BLACK) 00570 influence = v1 / (v1 + v2); 00571 else 00572 influence = v2 / (v1 + v2); 00573 00574 cmd << " " << *it << " " 00575 << std::fixed << std::setprecision(2) << influence; 00576 } 00577 } 00578 00579 //---------------------------------------------------------------------------- 00580 00581 void BenzeneHtpEngine::CmdMiscDebug(HtpCommand& cmd) 00582 { 00583 // cmd.CheckNuArg(1); 00584 // HexPoint point = HtpUtil::MoveArg(cmd, 0); 00585 cmd << *m_pe.brd << '\n'; 00586 } 00587 00588 //----------------------------------------------------------------------------