00001
00002
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)),
00035 m_dfpnHashTable(new DfpnHashTable(21)),
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
00104
00105
00106
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
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
00143
00144
00145
00146
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
00189
00190
00191
00192 if (!cur->HasNodeMove() && !cur->HasSon())
00193 break;
00194
00195
00196
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
00226
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
00243
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
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
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
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
00321
00322
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
00330
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
00341
00342
00343
00344
00345
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
00359
00360
00361
00362
00363 void BenzeneHtpEngine::CmdEncodePattern(HtpCommand& cmd)
00364 {
00365 HexAssert(cmd.NuArg() > 0);
00366
00367
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)
00411 sliceNo = 3;
00412 else if ((x2 < 0))
00413 sliceNo = 4;
00414 else
00415 sliceNo = 5;
00416 }
00417 else
00418 {
00419 if ((x2 + y2) > 0)
00420 sliceNo = 0;
00421 else if (x2 > 0)
00422 sliceNo = 1;
00423 else if (x2 < 0 && y2 == 0)
00424 sliceNo = 3;
00425 else
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;
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
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
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
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
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
00584
00585 cmd << *m_pe.brd << '\n';
00586 }
00587
00588