00001
00002
00003
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
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
00037
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 }
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
00068
00069 GoInitCheck();
00070 InitFromRoot(0, true);
00071 }
00072
00073 GoGameRecord::~GoGameRecord()
00074 {
00075
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
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
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
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
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
00152
00153
00154 return SgNodeUtil::GetMoveNumber(m_current);
00155 }
00156
00157 void GoGameRecord::DeleteTreeAndInitState()
00158 {
00159
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
00220
00221 {
00222 DeleteTreeAndInitState();
00223 OnInitBoard(size, rules);
00224
00225 SgNode* root = new SgNode();
00226 m_ownsTree = true;
00227
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
00236
00237
00238 void GoGameRecord::InitFromRoot(SgNode* root, bool fTakeOwnership)
00239 {
00240 DeleteTreeAndInitState();
00241 if (root)
00242 {
00243 m_ownsTree = fTakeOwnership;
00244
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
00259 root = new SgNode();
00260 m_ownsTree = true;
00261
00262
00263 SgPropInt* boardSize = new SgPropInt(SG_PROP_SIZE, m_board.Size());
00264 root->Add(boardSize);
00265 }
00266
00267
00268 const int GAME_ID = 1;
00269 SgPropInt* gameId = new SgPropInt(SG_PROP_GAME, GAME_ID);
00270 root->Add(gameId);
00271
00272
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
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
00337 SgPropPlayer* player =
00338 new SgPropPlayer(SG_PROP_PLAYER, SG_WHITE);
00339 root->Add(player);
00340 }
00341 }
00342 else
00343 {
00344
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
00360 m_current->Add(new SgPropPlayer(SG_PROP_PLAYER, player));
00361
00362
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(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
00488 SgNode* node = AddMove(move, player);
00489 GoToNode(node);
00490 return true;
00491 }
00492
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