00001 //---------------------------------------------------------------------------- 00002 /** @file GoGame.cpp 00003 See GoGame.h 00004 */ 00005 //---------------------------------------------------------------------------- 00006 00007 #include "SgSystem.h" 00008 #include "GoGame.h" 00009 00010 #include "GoBoardUtil.h" 00011 #include "GoInit.h" 00012 #include "GoPlayer.h" 00013 #include "SgNode.h" 00014 #include "SgNodeUtil.h" 00015 #include "SgProp.h" 00016 #include "SgSearchStatistics.h" 00017 #include "SgUtil.h" 00018 00019 using namespace std; 00020 using GoBoardUtil::PlayIfLegal; 00021 using SgUtil::ForceInRange; 00022 00023 //---------------------------------------------------------------------------- 00024 00025 namespace { 00026 00027 void AddStatisticsToNode(const SgSearchStatistics* stat, SgNode* node) 00028 { 00029 node->Add(new SgPropInt(SG_PROP_NUM_NODES, stat->NumNodes())); 00030 node->Add(new SgPropInt(SG_PROP_NUM_LEAFS, stat->NumEvals())); 00031 // AR: moves, pass moves 00032 node->Add(new SgPropMSec(SG_PROP_TIME_USED, stat->TimeUsed())); 00033 node->Add(new SgPropInt(SG_PROP_MAX_DEPTH, stat->DepthReached())); 00034 } 00035 00036 /** Add up to 4 handicap stones to '*stones', and reduce '*handicap' 00037 by that amount. 00038 */ 00039 void AddHandicap(int size, int row, int col, int* handicap, 00040 SgVector<SgPoint>* stones) 00041 { 00042 SG_ASSERT(2 <= *handicap); 00043 stones->PushBack(SgPointUtil::Pt(size + 1 - col, row)); 00044 stones->PushBack(SgPointUtil::Pt(col, size + 1 - row)); 00045 if (2 < *handicap) 00046 stones->PushBack(SgPointUtil::Pt(row, col)); 00047 if (3 < *handicap) 00048 stones->PushBack(SgPointUtil::Pt(size + 1 - row, size + 1 - col)); 00049 if (*handicap < 4) 00050 *handicap = 0; 00051 else 00052 *handicap -= 4; 00053 } 00054 00055 } // namespace 00056 00057 //---------------------------------------------------------------------------- 00058 00059 GoGameRecord::GoGameRecord(GoBoard& board) 00060 : m_current(0), 00061 m_board(board), 00062 m_time(), 00063 m_ownsTree(true), 00064 m_oldCommentNode(0), 00065 m_numMovesToInsert(0) 00066 { 00067 // Make sure GoInit was called to avoid silent failure of ExecuteMove 00068 // because of unregistered move property 00069 GoInitCheck(); 00070 InitFromRoot(0, true); 00071 } 00072 00073 GoGameRecord::~GoGameRecord() 00074 { 00075 // AR: go to 0 first?? 00076 if (m_current && m_ownsTree) 00077 m_current->DeleteTree(); 00078 } 00079 00080 00081 SgNode* GoGameRecord::AddMove(SgMove move, SgBlackWhite player, 00082 const SgSearchStatistics* stat) 00083 { 00084 // Check whether a node with that move already exists. 00085 SgNode* node = m_current->LeftMostSon(); 00086 while (node) 00087 { 00088 SgPropMove* prop = static_cast<SgPropMove*>(node->Get(SG_PROP_MOVE)); 00089 if (prop && prop->IsValue(move) && prop->IsPlayer(player)) 00090 break; 00091 node = node->RightBrother(); 00092 } 00093 00094 // If no such node exists, create a new node with the given move. 00095 if (! node) 00096 { 00097 if (m_current->HasSon() && 0 < m_numMovesToInsert) 00098 { 00099 node = m_current->LeftMostSon()->NewFather(); 00100 --m_numMovesToInsert; 00101 } 00102 else 00103 { 00104 node = m_current->NewRightMostSon(); 00105 m_numMovesToInsert = 0; 00106 } 00107 node->AddMoveProp(move, player); 00108 } 00109 // Add statistics and time left to the node. 00110 if (stat) 00111 AddStatisticsToNode(stat, node); 00112 m_time.PlayedMove(*node, player); 00113 return node; 00114 } 00115 00116 SgNode* GoGameRecord::AddResignNode(SgBlackWhite player) 00117 { 00118 SgNode* node = m_current->NewRightMostSon(); 00119 ostringstream comment; 00120 comment << (player == SG_BLACK ? "Black" : "White") << " resigned"; 00121 node->AddComment(comment.str()); 00122 return node; 00123 } 00124 00125 bool GoGameRecord::CanDeleteCurrentNode() const 00126 { 00127 return CurrentNode() && CurrentNode()->HasFather(); 00128 } 00129 00130 bool GoGameRecord::CanGoInDirection(SgNode::Direction dir) const 00131 { 00132 SgNode* node = m_current->NodeInDirection(dir); 00133 return node && node != m_current; 00134 } 00135 00136 SgMove GoGameRecord::CurrentMove() const 00137 { 00138 const SgNode* node = CurrentNode(); 00139 if (node) 00140 { 00141 // Get the move from the property. 00142 SgPropMove* prop = static_cast<SgPropMove*>(node->Get(SG_PROP_MOVE)); 00143 if (prop) 00144 return prop->Value(); 00145 } 00146 return SG_NULLMOVE; 00147 } 00148 00149 int GoGameRecord::CurrentMoveNumber() const 00150 { 00151 // TODO: once the transition of GoBoard to only support setup stones 00152 // in the initial position is finished, it will be more efficient to 00153 // call m_board.MoveNumber() instead of SgNodeUtil::GetMoveNumber() 00154 return SgNodeUtil::GetMoveNumber(m_current); 00155 } 00156 00157 void GoGameRecord::DeleteTreeAndInitState() 00158 { 00159 // Delete previous game tree if we own it. 00160 if (m_current && m_ownsTree) 00161 m_current->DeleteTree(); 00162 00163 m_current = 0; 00164 m_oldCommentNode = 0; 00165 } 00166 00167 bool GoGameRecord::EndOfGame() const 00168 { 00169 return GoBoardUtil::EndOfGame(m_board); 00170 } 00171 00172 std::string GoGameRecord::GetPlayerName(SgBlackWhite player) const 00173 { 00174 SgPropID playerNamePropID = 00175 SgProp::PlayerProp(SG_PROP_PLAYER_BLACK, player); 00176 SgNode* node = CurrentNode()->TopProp(playerNamePropID); 00177 if (node && node->HasProp(playerNamePropID)) 00178 { 00179 SgPropText* prop = 00180 dynamic_cast<SgPropText*>(node->Get(playerNamePropID)); 00181 return prop->Value(); 00182 } 00183 else 00184 return ""; 00185 } 00186 00187 bool GoGameRecord::GetScore(int* score) const 00188 { 00189 SG_ASSERT(score); 00190 SgNode* node = CurrentNode()->TopProp(SG_PROP_VALUE); 00191 if (node && node->HasProp(SG_PROP_VALUE)) 00192 { 00193 *score = node->GetIntProp(SG_PROP_VALUE); 00194 return true; 00195 } 00196 else 00197 return false; 00198 } 00199 00200 void GoGameRecord::GoInDirection(SgNode::Direction dir) 00201 { 00202 SgNode* node = m_current->NodeInDirection(dir); 00203 if (node != m_current) 00204 GoToNode(node); 00205 } 00206 00207 void GoGameRecord::GoToNode(SgNode* dest) 00208 { 00209 m_updater.Update(dest, m_board); 00210 SgNodeUtil::UpdateTime(Time(), dest); 00211 m_current = dest; 00212 if (GoBoardUtil::RemainingChineseHandicap(m_board)) 00213 m_board.SetToPlay(SG_BLACK); 00214 m_time.EnterNode(*m_current, m_board.ToPlay()); 00215 OnGoToNode(dest); 00216 } 00217 00218 void GoGameRecord::Init(int size, const GoRules& rules) 00219 // Delete the old game record and start with a fresh one. Init the board 00220 // to the given size and handicap, and create a root node to start with. 00221 { 00222 DeleteTreeAndInitState(); 00223 OnInitBoard(size, rules); 00224 // Create a new game tree, use current board. 00225 SgNode* root = new SgNode(); 00226 m_ownsTree = true; 00227 // Add root property: board size. 00228 SgPropInt* boardSize = new SgPropInt(SG_PROP_SIZE, m_board.Size()); 00229 root->Add(boardSize); 00230 OnInitHandicap(rules, root); 00231 GoToNode(root); 00232 } 00233 00234 /** 00235 @todo get handicap from properties, not from board 00236 @todo get komi from properties, not from board 00237 */ 00238 void GoGameRecord::InitFromRoot(SgNode* root, bool fTakeOwnership) 00239 { 00240 DeleteTreeAndInitState(); 00241 if (root) 00242 { 00243 m_ownsTree = fTakeOwnership; 00244 // Get board properties from root node. 00245 int size = GO_DEFAULT_SIZE; 00246 SgPropInt* boardSize = 00247 static_cast<SgPropInt*>(root->Get(SG_PROP_SIZE)); 00248 if (boardSize) 00249 { 00250 size = boardSize->Value(); 00251 ForceInRange(SG_MIN_SIZE, &size, SG_MAX_SIZE); 00252 } 00253 const GoRules& rules = m_board.Rules(); 00254 OnInitBoard(size, GoRules(rules.Handicap(), rules.Komi())); 00255 } 00256 else 00257 { 00258 // Create a new game tree, use current board. 00259 root = new SgNode(); 00260 m_ownsTree = true; 00261 00262 // Add root property: board size. 00263 SgPropInt* boardSize = new SgPropInt(SG_PROP_SIZE, m_board.Size()); 00264 root->Add(boardSize); 00265 } 00266 00267 // Add root property: Go game identifier. 00268 const int GAME_ID = 1; 00269 SgPropInt* gameId = new SgPropInt(SG_PROP_GAME, GAME_ID); 00270 root->Add(gameId); 00271 00272 // Go to the root node. 00273 GoToNode(root); 00274 } 00275 00276 void GoGameRecord::OnGoToNode(SgNode* dest) 00277 { 00278 SG_UNUSED(dest); 00279 } 00280 00281 void GoGameRecord::OnInitBoard(int size, const GoRules& rules) 00282 { 00283 m_board.Init(size, rules); 00284 } 00285 00286 void GoGameRecord::OnInitHandicap(const GoRules& rules, SgNode* root) 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 GoGameRecord::SetToPlay(SgBlackWhite player) 00353 { 00354 if (player != m_board.ToPlay()) 00355 { 00356 m_board.SetToPlay(player); 00357 if (m_current) 00358 { 00359 // Also record this change of player in the move tree. 00360 m_current->Add(new SgPropPlayer(SG_PROP_PLAYER, player)); 00361 00362 // Update the clock. 00363 m_time.EnterNode(*m_current, player); 00364 } 00365 } 00366 } 00367 00368 //---------------------------------------------------------------------------- 00369 00370 bool GoGameUtil::GotoBeforeMove(GoGameRecord* game, int moveNumber) 00371 { 00372 SG_ASSERT(game->CurrentNode() == &game->Root()); 00373 SG_ASSERT(moveNumber == -1 || moveNumber > 0); 00374 if (moveNumber > 0) 00375 { 00376 while (game->CanGoInDirection(SgNode::NEXT) 00377 && ! game->CurrentNode()->HasProp(SG_PROP_MOVE) 00378 && ! game->CurrentNode()->LeftMostSon()->HasProp(SG_PROP_MOVE)) 00379 game->GoInDirection(SgNode::NEXT); 00380 while (game->CurrentMoveNumber() < moveNumber - 1 00381 && game->CanGoInDirection(SgNode::NEXT)) 00382 game->GoInDirection(SgNode::NEXT); 00383 if (game->CurrentMoveNumber() != moveNumber - 1) 00384 return false; 00385 } 00386 else 00387 { 00388 while (game->CanGoInDirection(SgNode::NEXT)) 00389 game->GoInDirection(SgNode::NEXT); 00390 } 00391 return true; 00392 } 00393 00394 //---------------------------------------------------------------------------- 00395 00396 GoGame::GoGame(GoBoard& board) 00397 : GoGameRecord(board), 00398 m_player(0) 00399 { 00400 } 00401 00402 GoGame::~GoGame() 00403 { 00404 DeletePlayer(SG_BLACK); 00405 DeletePlayer(SG_WHITE); 00406 } 00407 00408 bool GoGame::CanPlayHumanMove(SgMove move, SgBlackWhite player) const 00409 { 00410 return ( m_player[player] == 0 00411 || ! ClockIsRunning() 00412 ) 00413 && Board().IsLegal(move, player); 00414 } 00415 00416 void GoGame::DeletePlayer(SgBlackWhite color) 00417 { 00418 SG_ASSERT_BW(color); 00419 if (m_player[SG_BLACK] == m_player[SG_WHITE]) 00420 { 00421 delete m_player[SG_BLACK]; 00422 m_player[SG_BLACK] = 0; 00423 m_player[SG_WHITE] = 0; 00424 return; 00425 } 00426 delete m_player[color]; 00427 m_player[color] = 0; 00428 } 00429 00430 void GoGame::Init(int size, const GoRules& rules) 00431 { 00432 GoGameRecord::Init(size, rules); 00433 UpdatePlayers(); 00434 } 00435 00436 void GoGame::Init(SgNode* root, bool fTakeOwnership, bool fDeletePlayers) 00437 { 00438 if (fDeletePlayers) 00439 { 00440 DeletePlayer(SG_BLACK); 00441 DeletePlayer(SG_WHITE); 00442 } 00443 GoGameRecord::InitFromRoot(root, fTakeOwnership); 00444 UpdatePlayers(); 00445 } 00446 00447 void GoGame::Init(int size, const GoRules& rules, bool fDeletePlayers) 00448 { 00449 if (fDeletePlayers) 00450 { 00451 DeletePlayer(SG_BLACK); 00452 DeletePlayer(SG_WHITE); 00453 } 00454 GoGameRecord::Init(size, rules); 00455 UpdatePlayers(); 00456 } 00457 00458 void GoGame::OnGoToNode(SgNode* dest) 00459 { 00460 SG_UNUSED(dest); 00461 UpdatePlayers(); 00462 } 00463 00464 void GoGame::PlayComputerMove(const GoPlayerMove* playerMove) 00465 { 00466 SgBlackWhite toPlay = playerMove->Color(); 00467 SgPoint move = playerMove->Point(); 00468 SgNode* node = AddMove(move, toPlay, 0); 00469 GoToNode(node); 00470 } 00471 00472 GoPlayerMove GoGame::PlayOneMove(SgBlackWhite color) 00473 { 00474 TurnClockOn(false); 00475 SgTimeRecord time(/*fOneMoveOnly*/true, 20); 00476 GoPlayer* player = m_player[color]; 00477 UpdatePlayer(color); 00478 SgBlackWhite toPlay = Board().ToPlay(); 00479 SgMove move = player->GenMove(time, toPlay); 00480 return GoPlayerMove(toPlay, move); 00481 } 00482 00483 bool GoGame::PlayHumanMove(SgMove move, SgBlackWhite player) 00484 { 00485 if (CanPlayHumanMove(move, player)) 00486 { 00487 // Add the move to the tree. 00488 SgNode* node = AddMove(move, player); 00489 GoToNode(node); 00490 return true; 00491 } 00492 // Illegal move, or not human player's turn to play. 00493 return false; 00494 } 00495 00496 void GoGame::SetPlayer(SgBlackWhite color, GoPlayer* player) 00497 { 00498 SG_ASSERT_BW(color); 00499 if (! (m_player[SG_BLACK] == m_player[SG_WHITE])) 00500 DeletePlayer(color); 00501 m_player[color] = player; 00502 UpdatePlayers(); 00503 } 00504 00505 void GoGame::TurnClockOn(bool turnOn) 00506 { 00507 Time().TurnClockOn(turnOn); 00508 } 00509 00510 void GoGame::UpdatePlayer(SgBlackWhite color) 00511 { 00512 GoPlayer* player = m_player[color]; 00513 if (player == 0) 00514 return; 00515 player->UpdateSubscriber(); 00516 player->SetCurrentNode(CurrentNode()); 00517 } 00518 00519 void GoGame::UpdatePlayers() 00520 { 00521 UpdatePlayer(SG_BLACK); 00522 if (m_player[SG_BLACK] != m_player[SG_WHITE]) 00523 UpdatePlayer(SG_WHITE); 00524 } 00525 00526 //---------------------------------------------------------------------------- 00527