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 //----------------------------------------------------------------------------