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


17 Jun 2010 Doxygen 1.4.7