Index   Main   Namespaces   Classes   Hierarchy   Annotated   Files   Compound   Global   Pages  

GoGame.cpp

Go to the documentation of this file.
00001 //----------------------------------------------------------------------------
00002 /** @file GoGame.cpp
00003     See GoGame.h */
00004 //----------------------------------------------------------------------------
00005 
00006 #include "SgSystem.h"
00007 #include "GoGame.h"
00008 
00009 #include "GoBoardUtil.h"
00010 #include "GoInit.h"
00011 #include "GoPlayer.h"
00012 #include "SgNode.h"
00013 #include "SgNodeUtil.h"
00014 #include "SgProp.h"
00015 #include "SgSearchStatistics.h"
00016 #include "SgUtil.h"
00017 
00018 using namespace std;
00019 using GoBoardUtil::PlayIfLegal;
00020 using SgUtil::ForceInRange;
00021 
00022 //----------------------------------------------------------------------------
00023 
00024 namespace {
00025 
00026 void AddStatisticsToNode(const SgSearchStatistics* stat, SgNode* node)
00027 {
00028     node->Add(new SgPropInt(SG_PROP_NUM_NODES, stat->NumNodes()));
00029     node->Add(new SgPropInt(SG_PROP_NUM_LEAFS, stat->NumEvals()));
00030     // AR: moves, pass moves
00031     node->Add(new SgPropMSec(SG_PROP_TIME_USED, stat->TimeUsed()));
00032     node->Add(new SgPropInt(SG_PROP_MAX_DEPTH, stat->DepthReached()));
00033 }
00034 
00035 /** Add up to 4 handicap stones to '*stones', and reduce '*handicap'
00036     by that amount. */
00037 void AddHandicap(int size, int row, int col, int* handicap,
00038                  SgVector<SgPoint>* stones)
00039 {
00040     SG_ASSERT(2 <= *handicap);
00041     stones->PushBack(SgPointUtil::Pt(size + 1 - col, row));
00042     stones->PushBack(SgPointUtil::Pt(col, size + 1 - row));
00043     if (2 < *handicap)
00044         stones->PushBack(SgPointUtil::Pt(row, col));
00045     if (3 < *handicap)
00046         stones->PushBack(SgPointUtil::Pt(size + 1 - row, size + 1 - col));
00047     if (*handicap < 4)
00048         *handicap = 0;
00049     else
00050         *handicap -= 4;
00051 }
00052 
00053 } // namespace
00054 
00055 //----------------------------------------------------------------------------
00056 
00057 GoGame::GoGame(int boardSize)
00058     : m_board(boardSize),
00059       m_root(new SgNode()),
00060       m_time(),
00061       m_numMovesToInsert(0)
00062 {
00063     // Make sure GoInit was called to avoid silent failure of ExecuteMove
00064     // because of unregistered move property
00065     GoInitCheck();
00066     Init(boardSize, GoRules());
00067 }
00068 
00069 GoGame::~GoGame()
00070 {
00071     m_root->DeleteTree();
00072 #ifndef NDEBUG
00073     m_root = 0;
00074 #endif
00075 }
00076 
00077 void GoGame::AddComment(const SgNode& node, const std::string& comment)
00078 {
00079     NonConstNodeRef(node).AddComment(comment);
00080 }
00081 
00082 void GoGame::AddMove(SgMove move, SgBlackWhite player,
00083                      const SgSearchStatistics* stat, bool makeMainVariation)
00084 {
00085     // Check whether a node with that move already exists.
00086     SgNode* node = m_current->LeftMostSon();
00087     while (node)
00088     {
00089         SgPropMove* prop = static_cast<SgPropMove*>(node->Get(SG_PROP_MOVE));
00090         if (prop && prop->IsValue(move) && prop->IsPlayer(player))
00091             break;
00092         node = node->RightBrother();
00093     }
00094 
00095     // If no such node exists, create a new node with the given move.
00096     if (! node)
00097     {
00098         if (m_current->HasSon() && 0 < m_numMovesToInsert)
00099         {
00100             node = m_current->LeftMostSon()->NewFather();
00101             --m_numMovesToInsert;
00102         }
00103         else
00104         {
00105             node = m_current->NewRightMostSon();
00106             m_numMovesToInsert = 0;
00107         }
00108         node->AddMoveProp(move, player);
00109     }
00110     // Add statistics and time left to the node.
00111     if (stat)
00112         AddStatisticsToNode(stat, node);
00113     m_time.PlayedMove(*node, player);
00114     if (makeMainVariation)
00115         node->PromoteNode();
00116     GoToNode(node);
00117 }
00118 
00119 const SgNode& GoGame::AddResignNode(SgBlackWhite player)
00120 {
00121     SgNode& node = *m_current->NewRightMostSon();
00122     ostringstream comment;
00123     comment << (player == SG_BLACK ? "Black" : "White") << " resigned";
00124     node.AddComment(comment.str());
00125     return node;
00126 }
00127 
00128 void GoGame::AppendChild(SgNode* child)
00129 {
00130     child->AppendTo(m_current);
00131 }
00132 
00133 bool GoGame::CanGoInDirection(SgNode::Direction dir) const
00134 {
00135     SgNode* node = m_current->NodeInDirection(dir);
00136     return node && node != m_current;
00137 }
00138 
00139 SgMove GoGame::CurrentMove() const
00140 {
00141     SgPropMove* prop = static_cast<SgPropMove*>(m_current->Get(SG_PROP_MOVE));
00142     if (prop)
00143         return prop->Value();
00144     return SG_NULLMOVE;
00145 }
00146 
00147 int GoGame::CurrentMoveNumber() const
00148 {
00149     // TODO: once the transition of GoBoard to only support setup stones
00150     // in the initial position is finished, it will be more efficient to
00151     // call m_board.MoveNumber() instead of SgNodeUtil::GetMoveNumber()
00152     return SgNodeUtil::GetMoveNumber(m_current);
00153 }
00154 
00155 bool GoGame::EndOfGame() const
00156 {
00157     return GoBoardUtil::EndOfGame(m_board);
00158 }
00159 
00160 /** Find the game info node with a game info property that determines
00161     this property for the current node.
00162     Returns an empty string, if no such node exists. */
00163 std::string GoGame::GetGameInfoStringProp(SgPropID id) const
00164 {
00165     const SgNode* node = m_current->TopProp(id);
00166     if (node->HasProp(id))
00167     {
00168         const SgPropText* prop = dynamic_cast<SgPropText*>(node->Get(id));
00169         return prop->Value();
00170     }
00171     else
00172         return "";
00173 }
00174 
00175 std::string GoGame::GetGameName() const
00176 {
00177     return GetGameInfoStringProp(SG_PROP_GAME_NAME);
00178 }
00179 
00180 std::string GoGame::GetPlayerName(SgBlackWhite player) const
00181 {
00182     SgPropID id = SgProp::PlayerProp(SG_PROP_PLAYER_BLACK, player);
00183     return GetGameInfoStringProp(id);
00184 }
00185 
00186 std::string GoGame::GetResult() const
00187 {
00188     return GetGameInfoStringProp(SG_PROP_RESULT);
00189 }
00190 
00191 void GoGame::GoInDirection(SgNode::Direction dir)
00192 {
00193     SgNode* node = m_current->NodeInDirection(dir);
00194     if (node != m_current)
00195         GoToNode(node);
00196 }
00197 
00198 void GoGame::GoToNode(const SgNode* dest)
00199 {
00200     m_updater.Update(dest, m_board);
00201     SgNodeUtil::UpdateTime(Time(), dest);
00202     m_current = NonConstNodePtr(dest);
00203     if (GoBoardUtil::RemainingChineseHandicap(m_board))
00204         m_board.SetToPlay(SG_BLACK);
00205     m_time.EnterNode(*m_current, m_board.ToPlay());
00206 }
00207 
00208 void GoGame::Init(int size, const GoRules& rules)
00209 {
00210     m_board.Init(size, rules);
00211     m_root->DeleteTree();
00212     m_root = new SgNode();
00213     SgPropInt* boardSizeProp = new SgPropInt(SG_PROP_SIZE, size);
00214     m_root->Add(boardSizeProp);
00215     GoKomi komi = rules.Komi();
00216     if (! komi.IsUnknown())
00217         m_root->SetRealProp(SG_PROP_KOMI, komi.ToFloat(), 1);
00218     InitHandicap(rules, m_root);
00219     GoToNode(m_root);
00220 }
00221 
00222 void GoGame::Init(SgNode* root)
00223 {
00224     m_root->DeleteTree();
00225     m_root = root;
00226     int size = GO_DEFAULT_SIZE;
00227     SgPropInt* boardSizeProp =
00228         static_cast<SgPropInt*>(m_root->Get(SG_PROP_SIZE));
00229     if (boardSizeProp)
00230     {
00231         size = boardSizeProp->Value();
00232         ForceInRange(SG_MIN_SIZE, &size, SG_MAX_SIZE);
00233     }
00234     const GoRules& rules = m_board.Rules();
00235     m_board.Init(size, GoRules(rules.Handicap(), rules.Komi()));
00236 
00237     // Add root property: Go game identifier.
00238     const int GAME_ID = 1;
00239     SgPropInt* gameId = new SgPropInt(SG_PROP_GAME, GAME_ID);
00240     m_root->Add(gameId);
00241 
00242     // Go to the root node.
00243     GoToNode(m_root);
00244 }
00245 
00246 /** Convert a const reference of a node received by the user of this class
00247     to a non-const reference.
00248     The user should not be able to modify the game tree directly, so he can
00249     only be given const references to nodes. This function exists to convert
00250     such a user reference to non-const for internal usage and avoids the
00251     spreading of const casts all over the code. It also contains an assertion
00252     that the node is part of the current game tree. */
00253 SgNode* GoGame::NonConstNodePtr(const SgNode* node) const
00254 {
00255     SG_ASSERT(node->Root() == m_root);
00256     return const_cast<SgNode*>(node);
00257 }
00258 
00259 /** See NonConstNodePtr(). */
00260 SgNode& GoGame::NonConstNodeRef(const SgNode& node) const
00261 {
00262     SG_ASSERT(node.Root() == m_root);
00263     return const_cast<SgNode&>(node);
00264 }
00265 
00266 void GoGame::PlaceHandicap(const SgVector<SgPoint>& stones)
00267 {
00268     SG_ASSERT(GoBoardUtil::IsBoardEmpty(m_board));
00269     SgNode* node = m_current;
00270     if (node->HasSon())
00271         node = node->NewRightMostSon();
00272     SgPropAddStone* addBlack = new SgPropAddStone(SG_PROP_ADD_BLACK);
00273     for (SgVectorIterator<SgPoint> it(stones); it; ++it)
00274         addBlack->PushBack(*it);
00275     node->Add(addBlack);
00276     SgPropInt* handicap = new SgPropInt(SG_PROP_HANDICAP, stones.Length());
00277     node->Add(handicap);
00278     node->Add(new SgPropPlayer(SG_PROP_PLAYER, SG_WHITE));
00279     m_board.Rules().SetHandicap(stones.Length());
00280     GoToNode(node);
00281 }
00282 
00283 void GoGame::InitHandicap(const GoRules& rules, SgNode* root)
00284 {
00285     // TODO: Use PlaceHandicap() in implementation of InitHandicap() to
00286     // avoid redundancy
00287 
00288     // Add handicap properties.
00289     if (2 <= rules.Handicap())
00290     {
00291         SgPropInt* handicap =
00292             new SgPropInt(SG_PROP_HANDICAP, rules.Handicap());
00293         root->Add(handicap);
00294         if (rules.JapaneseHandicap())
00295         {
00296             if (9 <= m_board.Size())
00297             {
00298                 int h = rules.Handicap();
00299                 int half = (m_board.Size()+1) / 2;
00300                 SgVector<SgPoint> stones;
00301                 if ((4 < h) && (h % 2 != 0))
00302                 {
00303                     stones.PushBack(SgPointUtil::Pt(half, half));
00304                     --h;
00305                 }
00306                 if (13 <= m_board.Size())
00307                 {
00308                     AddHandicap(m_board.Size(), 4, 4, &h, &stones);
00309                     if (0 < h)
00310                         AddHandicap(m_board.Size(), half, 4, &h, &stones);
00311                     if (0 < h)
00312                         AddHandicap(m_board.Size(), 3, 3, &h, &stones);
00313                     if (0 < h)
00314                         AddHandicap(m_board.Size(), 7, 7, &h, &stones);
00315                     if (0 < h)
00316                         AddHandicap(m_board.Size(), half, 3, &h, &stones);
00317                     if (0 < h)
00318                         AddHandicap(m_board.Size(), half - (half - 4) / 2,
00319                                     4, &h, &stones);
00320                     if (0 < h)
00321                         AddHandicap(m_board.Size(), half + (half - 4) / 2,
00322                                     4, &h, &stones);
00323                 }
00324                 else
00325                 {
00326                     AddHandicap(m_board.Size(), 3, 3, &h, &stones);
00327                     if (0 < h)
00328                         AddHandicap(m_board.Size(), half, 3, &h, &stones);
00329                     if (0 < h)
00330                         AddHandicap(m_board.Size(), 4, 4, &h, &stones);
00331                 }
00332                 SgPropAddStone* addBlack =
00333                     new SgPropAddStone(SG_PROP_ADD_BLACK, stones);
00334                 root->Add(addBlack);
00335 
00336                 // White to play.
00337                 SgPropPlayer* player =
00338                     new SgPropPlayer(SG_PROP_PLAYER, SG_WHITE);
00339                 root->Add(player);
00340             }
00341         }
00342         else
00343         {
00344             // Chinese handicap.
00345             SgPropInt* chinese =
00346                 new SgPropInt(SG_PROP_CHINESE, rules.Handicap());
00347             root->Add(chinese);
00348         }
00349     }
00350 }
00351 
00352 void GoGame::SetKomiGlobal(GoKomi komi)
00353 {
00354     SgNodeUtil::RemovePropInSubtree(*m_root, SG_PROP_KOMI);
00355     if (! komi.IsUnknown())
00356         m_root->SetRealProp(SG_PROP_KOMI, komi.ToFloat(), 1);
00357     m_board.Rules().SetKomi(komi);
00358 }
00359 
00360 void GoGame::SetRulesGlobal(const GoRules& rules)
00361 {
00362     m_board.Rules() = rules;
00363     // TODO: Create description of rules and store it in RU property of root
00364     SetKomiGlobal(rules.Komi());
00365 }
00366 
00367 void GoGame::SetTimeSettingsGlobal(const GoTimeSettings& timeSettings,
00368                                    double overhead)
00369 {
00370     m_timeSettings = timeSettings;
00371     SgNodeUtil::RemovePropInSubtree(*m_root, SG_PROP_TIME);
00372     SgNodeUtil::RemovePropInSubtree(*m_root, SG_PROP_OT_NU_MOVES);
00373     SgNodeUtil::RemovePropInSubtree(*m_root, SG_PROP_OT_PERIOD);
00374     if (timeSettings.IsUnknown())
00375     {
00376         // TODO: What to do with m_time? What to do with time left properties
00377         // in tree nodes?
00378         return;
00379     }
00380     double mainTime = timeSettings.MainTime();
00381     m_root->Add(new SgPropTime(SG_PROP_TIME, mainTime));
00382     double overtime = timeSettings.Overtime();
00383     if (overtime > 0)
00384     {
00385         m_root->Add(new SgPropTime(SG_PROP_OT_PERIOD, overtime));
00386         m_root->SetIntProp(SG_PROP_OT_NU_MOVES, timeSettings.OvertimeMoves());
00387     }
00388     // TODO: What if the current node is not the root? What if nodes on the
00389     // path from the root to the current node contain time left properties?
00390     // Should we delete all time left properties or keep and still respect
00391     // them for setting the time left in the current position?
00392     m_time.SetOTPeriod(overtime);
00393     m_time.SetOTNumMoves(timeSettings.OvertimeMoves());
00394     m_time.SetOverhead(overhead);
00395     m_time.SetClock(*m_current, SG_BLACK, mainTime);
00396     m_time.SetClock(*m_current, SG_WHITE, mainTime);
00397     m_time.TurnClockOn(true);
00398 }
00399 
00400 void GoGame::SetToPlay(SgBlackWhite toPlay)
00401 {
00402     if (toPlay == m_board.ToPlay())
00403         return;
00404     m_board.SetToPlay(toPlay);
00405     m_current->Add(new SgPropPlayer(SG_PROP_PLAYER, toPlay));
00406     m_time.EnterNode(*m_current, toPlay);
00407 }
00408 
00409 void GoGame::SetupPosition(const SgBWArray<SgPointSet>& stones)
00410 {
00411     SgPropAddStone* addBlack = new SgPropAddStone(SG_PROP_ADD_BLACK);
00412     SgPropAddStone* addWhite = new SgPropAddStone(SG_PROP_ADD_WHITE);
00413     for (SgSetIterator it(stones[SG_BLACK]); it; ++it)
00414         if (m_board.GetColor(*it) != SG_BLACK)
00415             addBlack->PushBack(*it);
00416     for (SgSetIterator it(stones[SG_WHITE]); it; ++it)
00417         if (m_board.GetColor(*it) != SG_WHITE)
00418             addWhite->PushBack(*it);
00419     SgNode* node = m_current->NewRightMostSon();
00420     node->Add(addBlack);
00421     node->Add(addWhite);
00422     GoToNode(node);
00423 }
00424 
00425 void GoGame::UpdateDate(const std::string& date)
00426 {
00427     UpdateGameInfoStringProp(SG_PROP_DATE, date);
00428 }
00429 
00430 void GoGame::UpdateGameInfoStringProp(SgPropID id, const std::string& value)
00431 {
00432     SgNode* node = m_current->TopProp(id);
00433     node->SetStringProp(id, value);
00434 }
00435 
00436 void GoGame::UpdateGameName(const std::string& name)
00437 {
00438     UpdateGameInfoStringProp(SG_PROP_GAME_NAME, name);
00439 }
00440 
00441 void GoGame::UpdatePlayerName(SgBlackWhite color, const std::string& name)
00442 {
00443     SgPropID id = SgProp::PlayerProp(SG_PROP_PLAYER_BLACK, color);
00444     UpdateGameInfoStringProp(id, name);
00445 }
00446 
00447 void GoGame::UpdateResult(const std::string& result)
00448 {
00449     UpdateGameInfoStringProp(SG_PROP_RESULT, result);
00450 }
00451 
00452 //----------------------------------------------------------------------------
00453 
00454 bool GoGameUtil::GotoBeforeMove(GoGame* game, int moveNumber)
00455 {
00456     SG_ASSERT(game->CurrentNode() == &game->Root());
00457     SG_ASSERT(moveNumber == -1 || moveNumber > 0);
00458     if (moveNumber > 0)
00459     {
00460         while (game->CanGoInDirection(SgNode::NEXT)
00461                && ! game->CurrentNode()->HasProp(SG_PROP_MOVE)
00462                && ! game->CurrentNode()->LeftMostSon()->HasProp(SG_PROP_MOVE))
00463             game->GoInDirection(SgNode::NEXT);
00464         while (game->CurrentMoveNumber() < moveNumber - 1
00465                && game->CanGoInDirection(SgNode::NEXT))
00466             game->GoInDirection(SgNode::NEXT);
00467         if (game->CurrentMoveNumber() != moveNumber - 1)
00468             return false;
00469     }
00470     else
00471     {
00472         while (game->CanGoInDirection(SgNode::NEXT))
00473             game->GoInDirection(SgNode::NEXT);
00474     }
00475     return true;
00476 }
00477 
00478 //----------------------------------------------------------------------------


Sun Mar 13 2011 Doxygen 1.7.1