00001 //---------------------------------------------------------------------------- 00002 /** @file GoBoard.cpp 00003 See GoBoard.h */ 00004 //---------------------------------------------------------------------------- 00005 00006 #include "SgSystem.h" 00007 #include "GoBoard.h" 00008 00009 #include <boost/static_assert.hpp> 00010 #include <algorithm> 00011 #include "GoInit.h" 00012 #include "SgNbIterator.h" 00013 #include "SgStack.h" 00014 00015 using namespace std; 00016 00017 //---------------------------------------------------------------------------- 00018 00019 namespace { 00020 00021 /** Do a consistency check. 00022 Check some data structures for consistency after and before each play 00023 and undo (and at some other places). 00024 This is an expensive check and therefore has to be enabled at compile 00025 time. */ 00026 const bool CONSISTENCY = false; 00027 00028 void UpdateChanges(SgPoint p, SgArrayList<SgPoint,SG_MAXPOINT>& changes, 00029 int& nuChanges) 00030 { 00031 if (! changes.Exclude(p)) 00032 changes.PushBack(p); 00033 ++nuChanges; 00034 } 00035 00036 } // namespace 00037 00038 //---------------------------------------------------------------------------- 00039 00040 GoBoard::GoBoard(int size, const GoSetup& setup, const GoRules& rules) 00041 : m_snapshot(new Snapshot()), 00042 m_const(size), 00043 m_blockList(new SgArrayList<Block,GO_MAX_NUM_MOVES>()), 00044 m_moves(new SgArrayList<StackEntry,GO_MAX_NUM_MOVES>()) 00045 00046 { 00047 GoInitCheck(); 00048 Init(size, rules, setup); 00049 } 00050 00051 GoBoard::~GoBoard() 00052 { 00053 delete m_blockList; 00054 m_blockList = 0; 00055 delete m_moves; 00056 m_moves = 0; 00057 } 00058 00059 void GoBoard::CheckConsistency() const 00060 { 00061 if (! CONSISTENCY) 00062 return; 00063 int numberBlack = 0; 00064 int numberWhite = 0; 00065 for (SgPoint p = 0; p < SG_MAXPOINT; ++p) 00066 { 00067 if (IsBorder(p)) 00068 continue; 00069 int c = m_state.m_color[p]; 00070 SG_ASSERT_EBW(c); 00071 int n = 0; 00072 for (SgNb4Iterator it(p); it; ++it) 00073 if (m_state.m_color[*it] == SG_EMPTY) 00074 ++n; 00075 SG_ASSERT(n == NumEmptyNeighbors(p)); 00076 n = 0; 00077 for (SgNb4Iterator it(p); it; ++it) 00078 if (m_state.m_color[*it] == SG_BLACK) 00079 ++n; 00080 SG_ASSERT(n == NumNeighbors(p, SG_BLACK)); 00081 n = 0; 00082 for (SgNb4Iterator it(p); it; ++it) 00083 if (m_state.m_color[*it] == SG_WHITE) 00084 ++n; 00085 SG_ASSERT(n == NumNeighbors(p, SG_WHITE)); 00086 if (c == SG_BLACK || c == SG_WHITE) 00087 { 00088 SG_ASSERT(m_state.m_all[c].Contains(p)); 00089 CheckConsistencyBlock(p); 00090 } 00091 if (c == SG_BLACK) 00092 ++numberBlack; 00093 if (c == SG_WHITE) 00094 ++numberWhite; 00095 if (c == SG_EMPTY) 00096 SG_ASSERT(m_state.m_block[p] == 0); 00097 } 00098 SG_ASSERT(m_state.m_all[SG_BLACK].Size() == numberBlack); 00099 SG_ASSERT(m_state.m_all[SG_WHITE].Size() == numberWhite); 00100 } 00101 00102 void GoBoard::CheckConsistencyBlock(SgPoint point) const 00103 { 00104 SG_ASSERT(Occupied(point)); 00105 SgBlackWhite color = GetColor(point); 00106 GoPointList stones; 00107 Block::LibertyList liberties; 00108 SgMarker mark; 00109 SgStack<SgPoint,SG_MAXPOINT> stack; 00110 stack.Push(point); 00111 while (! stack.IsEmpty()) 00112 { 00113 SgPoint p = stack.Pop(); 00114 if (IsBorder(p) || ! mark.NewMark(p)) 00115 continue; 00116 if (GetColor(p) == color) 00117 { 00118 stones.PushBack(p); 00119 stack.Push(p - SG_NS); 00120 stack.Push(p - SG_WE); 00121 stack.Push(p + SG_WE); 00122 stack.Push(p + SG_NS); 00123 } 00124 else if (GetColor(p) == SG_EMPTY) 00125 liberties.PushBack(p); 00126 } 00127 const Block* block = m_state.m_block[point]; 00128 SG_DEBUG_ONLY(block); 00129 SG_ASSERT(stones.Contains(block->Anchor())); 00130 SG_ASSERT(color == block->Color()); 00131 SG_ASSERT(stones.SameElements(block->Stones())); 00132 SG_ASSERT(liberties.SameElements(block->Liberties())); 00133 SG_ASSERT(stones.Length() == NumStones(point)); 00134 } 00135 00136 bool GoBoard::CheckKo(SgBlackWhite player) 00137 { 00138 if (! FullBoardRepetition()) 00139 return true; 00140 m_moveInfo.set(GO_MOVEFLAG_REPETITION); 00141 if (AnyRepetitionAllowed()) 00142 { 00143 if (m_koLoser != SG_EMPTY && m_koLoser != player) 00144 ++m_state.m_koLevel; 00145 return true; 00146 } 00147 if (KoRepetitionAllowed() 00148 && (m_koLoser != player) 00149 && (! m_koModifiesHash || (m_state.m_koLevel < MAX_KOLEVEL)) 00150 && (m_koColor != SgOppBW(player))) 00151 { 00152 ++m_state.m_koLevel; 00153 m_koColor = player; 00154 if (m_koModifiesHash) 00155 m_state.m_hash.XorWinKo(m_state.m_koLevel, m_koColor); 00156 return true; 00157 } 00158 return false; 00159 } 00160 00161 void GoBoard::AddLibToAdjBlocks(SgPoint p) 00162 { 00163 if (NumNeighbors(p, SG_BLACK) + NumNeighbors(p, SG_WHITE) == 0) 00164 return; 00165 SgArrayList<Block*,4> blocks = GetAdjacentBlocks(p); 00166 for (SgArrayList<Block*,4>::Iterator it(blocks); it; ++it) 00167 { 00168 Block* block = *it; 00169 if (block != 0) 00170 block->AppendLiberty(p); 00171 } 00172 } 00173 00174 void GoBoard::AddLibToAdjBlocks(SgPoint p, SgBlackWhite c) 00175 { 00176 if (NumNeighbors(p, c) == 0) 00177 return; 00178 SgArrayList<Block*,4> blocks = GetAdjacentBlocks(p, c); 00179 for (SgArrayList<Block*,4>::Iterator it(blocks); it; ++it) 00180 { 00181 Block* block = *it; 00182 if (block != 0) 00183 block->AppendLiberty(p); 00184 } 00185 } 00186 00187 void GoBoard::AddStoneToBlock(SgPoint p, SgBlackWhite c, Block* block, 00188 StackEntry& entry) 00189 { 00190 SG_DEBUG_ONLY(c); 00191 // Stone already placed 00192 SG_ASSERT(IsColor(p, c)); 00193 block->AppendStone(p); 00194 entry.m_newLibs.Clear(); 00195 if (IsEmpty(p - SG_NS) && ! IsAdjacentTo(p - SG_NS, block)) 00196 { 00197 block->AppendLiberty(p - SG_NS); 00198 entry.m_newLibs.PushBack(p - SG_NS); 00199 } 00200 if (IsEmpty(p - SG_WE) && ! IsAdjacentTo(p - SG_WE, block)) 00201 { 00202 block->AppendLiberty(p - SG_WE); 00203 entry.m_newLibs.PushBack(p - SG_WE); 00204 } 00205 if (IsEmpty(p + SG_WE) && ! IsAdjacentTo(p + SG_WE, block)) 00206 { 00207 block->AppendLiberty(p + SG_WE); 00208 entry.m_newLibs.PushBack(p + SG_WE); 00209 } 00210 if (IsEmpty(p + SG_NS) && ! IsAdjacentTo(p + SG_NS, block)) 00211 { 00212 block->AppendLiberty(p + SG_NS); 00213 entry.m_newLibs.PushBack(p + SG_NS); 00214 } 00215 entry.m_oldAnchor = block->Anchor(); 00216 block->UpdateAnchor(p); 00217 m_state.m_block[p] = block; 00218 } 00219 00220 GoBoard::Block& GoBoard::CreateNewBlock() 00221 { 00222 // Reuse without initialization 00223 m_blockList->Resize(m_blockList->Length() + 1); 00224 Block& block = m_blockList->Last(); 00225 return block; 00226 } 00227 00228 void GoBoard::CreateSingleStoneBlock(SgPoint p, SgBlackWhite c) 00229 { 00230 // Stone already placed 00231 SG_ASSERT(IsColor(p, c)); 00232 SG_ASSERT(NumNeighbors(p, c) == 0); 00233 Block& block = CreateNewBlock(); 00234 block.Init(c, p); 00235 if (IsEmpty(p - SG_NS)) 00236 block.AppendLiberty(p - SG_NS); 00237 if (IsEmpty(p - SG_WE)) 00238 block.AppendLiberty(p - SG_WE); 00239 if (IsEmpty(p + SG_WE)) 00240 block.AppendLiberty(p + SG_WE); 00241 if (IsEmpty(p + SG_NS)) 00242 block.AppendLiberty(p + SG_NS); 00243 m_state.m_block[p] = █ 00244 } 00245 00246 SgArrayList<GoBoard::Block*,4> GoBoard::GetAdjacentBlocks(SgPoint p) const 00247 { 00248 SgArrayList<Block*,4> result; 00249 if (NumNeighbors(p, SG_BLACK) > 0 || NumNeighbors(p, SG_WHITE) > 0) 00250 { 00251 Block* block; 00252 if ((block = m_state.m_block[p - SG_NS]) != 0) 00253 result.PushBack(block); 00254 if ((block = m_state.m_block[p - SG_WE]) != 0 00255 && ! result.Contains(block)) 00256 result.PushBack(block); 00257 if ((block = m_state.m_block[p + SG_WE]) != 0 00258 && ! result.Contains(block)) 00259 result.PushBack(block); 00260 if ((block = m_state.m_block[p + SG_NS]) != 0 00261 && ! result.Contains(block)) 00262 result.PushBack(block); 00263 } 00264 return result; 00265 } 00266 00267 SgArrayList<GoBoard::Block*,4> GoBoard::GetAdjacentBlocks(SgPoint p, 00268 SgBlackWhite c) const 00269 { 00270 SgArrayList<Block*,4> result; 00271 if (NumNeighbors(p, c) > 0) 00272 { 00273 Block* block; 00274 if (IsColor(p - SG_NS, c)) 00275 result.PushBack(m_state.m_block[p - SG_NS]); 00276 if (IsColor(p - SG_WE, c) 00277 && ! result.Contains((block = m_state.m_block[p - SG_WE]))) 00278 result.PushBack(block); 00279 if (IsColor(p + SG_WE, c) 00280 && ! result.Contains((block = m_state.m_block[p + SG_WE]))) 00281 result.PushBack(block); 00282 if (IsColor(p + SG_NS, c) 00283 && ! result.Contains((block = m_state.m_block[p + SG_NS]))) 00284 result.PushBack(block); 00285 } 00286 return result; 00287 } 00288 00289 bool GoBoard::IsAdjacentTo(SgPoint p, const GoBoard::Block* block) const 00290 { 00291 return m_state.m_block[p - SG_NS] == block 00292 || m_state.m_block[p - SG_WE] == block 00293 || m_state.m_block[p + SG_WE] == block 00294 || m_state.m_block[p + SG_NS] == block; 00295 } 00296 00297 void GoBoard::MergeBlocks(SgPoint p, SgBlackWhite c, 00298 const SgArrayList<Block*,4>& adjBlocks) 00299 { 00300 // Stone already placed 00301 SG_ASSERT(IsColor(p, c)); 00302 SG_ASSERT(NumNeighbors(p, c) > 1); 00303 Block& block = CreateNewBlock(); 00304 block.Init(c, p); 00305 SgReserveMarker reserve(m_marker); 00306 SG_UNUSED(reserve); 00307 m_marker.Clear(); 00308 for (SgArrayList<Block*,4>::Iterator it(adjBlocks); it; ++it) 00309 { 00310 Block* adjBlock = *it; 00311 for (Block::StoneIterator stn(adjBlock->Stones()); stn; ++stn) 00312 { 00313 block.AppendStone(*stn); 00314 m_state.m_block[*stn] = █ 00315 } 00316 for (Block::LibertyIterator lib(adjBlock->Liberties()); lib; ++lib) 00317 if (m_marker.NewMark(*lib)) 00318 block.AppendLiberty(*lib); 00319 block.UpdateAnchor(adjBlock->Anchor()); 00320 } 00321 m_state.m_block[p] = █ 00322 if (IsEmpty(p - SG_NS) && m_marker.NewMark(p - SG_NS)) 00323 block.AppendLiberty(p - SG_NS); 00324 if (IsEmpty(p - SG_WE) && m_marker.NewMark(p - SG_WE)) 00325 block.AppendLiberty(p - SG_WE); 00326 if (IsEmpty(p + SG_WE) && m_marker.NewMark(p + SG_WE)) 00327 block.AppendLiberty(p + SG_WE); 00328 if (IsEmpty(p + SG_NS) && m_marker.NewMark(p + SG_NS)) 00329 block.AppendLiberty(p + SG_NS); 00330 } 00331 00332 void GoBoard::RemoveLibFromAdjBlocks(SgPoint p, SgBlackWhite c) 00333 { 00334 if (NumNeighbors(p, c) == 0) 00335 return; 00336 SgArrayList<Block*,4> blocks = GetAdjacentBlocks(p, c); 00337 for (SgArrayList<Block*,4>::Iterator it(blocks); it; ++it) 00338 (*it)->ExcludeLiberty(p); 00339 } 00340 00341 void GoBoard::RestoreKill(Block* block, SgBlackWhite c) 00342 { 00343 SgBlackWhite opp = SgOppBW(c); 00344 for (Block::StoneIterator it(block->Stones()); it; ++it) 00345 { 00346 SgPoint stn = *it; 00347 AddStone(stn, c); 00348 m_state.m_block[stn] = block; 00349 RemoveLibFromAdjBlocks(stn, opp); 00350 } 00351 int nuStones = block->Stones().Length(); 00352 m_state.m_numStones[c] += nuStones; 00353 m_state.m_prisoners[c] -= nuStones; 00354 SG_ASSERT(m_state.m_prisoners[c] >= 0); 00355 } 00356 00357 void GoBoard::UpdateBlocksAfterAddStone(SgPoint p, SgBlackWhite c, 00358 StackEntry& entry) 00359 { 00360 // Stone already placed 00361 SG_ASSERT(IsColor(p, c)); 00362 if (NumNeighbors(p, c) == 0) 00363 { 00364 entry.m_stoneAddedTo = 0; 00365 CreateSingleStoneBlock(p, c); 00366 entry.m_merged.Clear(); 00367 } 00368 else 00369 { 00370 SgArrayList<Block*,4> adjBlocks = GetAdjacentBlocks(p, c); 00371 if (adjBlocks.Length() == 1) 00372 { 00373 Block* block = adjBlocks[0]; 00374 AddStoneToBlock(p, c, block, entry); 00375 entry.m_stoneAddedTo = block; 00376 } 00377 else 00378 { 00379 entry.m_stoneAddedTo = 0; 00380 MergeBlocks(p, c, adjBlocks); 00381 entry.m_merged = adjBlocks; 00382 } 00383 } 00384 } 00385 00386 void GoBoard::UpdateBlocksAfterUndo(const StackEntry& entry) 00387 { 00388 SgPoint p = entry.m_point; 00389 if (IsPass(p)) 00390 return; 00391 SgBlackWhite c = entry.m_color; 00392 Block* block = entry.m_suicide; 00393 if (block != 0) 00394 RestoreKill(block, c); 00395 RemoveStone(p); 00396 --m_state.m_numStones[c]; 00397 m_state.m_block[p] = 0; 00398 Block* stoneAddedTo = entry.m_stoneAddedTo; 00399 if (stoneAddedTo != 0) 00400 { 00401 stoneAddedTo->PopStone(); 00402 for (SgArrayList<SgPoint,4>::Iterator it(entry.m_newLibs); it; ++it) 00403 stoneAddedTo->ExcludeLiberty(*it); 00404 stoneAddedTo->SetAnchor(entry.m_oldAnchor); 00405 } 00406 else 00407 { 00408 for (SgArrayList<Block*,4>::Iterator it(entry.m_merged); it; ++it) 00409 { 00410 Block* block = *it; 00411 for (Block::StoneIterator stn(block->Stones()); stn; ++stn) 00412 m_state.m_block[*stn] = block; 00413 } 00414 m_blockList->PopBack(); 00415 } 00416 for (SgArrayList<Block*,4>::Iterator it(entry.m_killed); it; ++it) 00417 RestoreKill(*it, SgOppBW(entry.m_color)); 00418 AddLibToAdjBlocks(p); 00419 } 00420 00421 void GoBoard::Init(int size, const GoRules& rules, const GoSetup& setup) 00422 { 00423 m_rules = rules; 00424 m_size = size; 00425 SG_ASSERTRANGE(m_size, SG_MIN_SIZE, SG_MAX_SIZE); 00426 m_state.m_hash.Clear(); 00427 m_moves->Clear(); 00428 m_state.m_prisoners[SG_BLACK] = 0; 00429 m_state.m_prisoners[SG_WHITE] = 0; 00430 m_state.m_numStones[SG_BLACK] = 0; 00431 m_state.m_numStones[SG_WHITE] = 0; 00432 m_countPlay = 0; 00433 m_state.m_koPoint = SG_NULLPOINT; 00434 m_allowAnyRepetition = false; 00435 m_allowKoRepetition = false; 00436 m_koColor = SG_EMPTY; 00437 m_koLoser = SG_EMPTY; 00438 m_state.m_koLevel = 0; 00439 m_koModifiesHash = true; 00440 m_const.ChangeSize(m_size); 00441 for (SgPoint p = 0; p < SG_MAXPOINT; ++p) 00442 { 00443 m_state.m_color[p] = SG_BORDER; 00444 m_isBorder[p] = true; 00445 } 00446 m_state.m_isFirst.Fill(true); 00447 m_state.m_isNewPosition = true; 00448 for (SgGrid row = 1; row <= m_size; ++row) 00449 for (SgGrid col = 1; col <= m_size; ++col) 00450 { 00451 SgPoint p = SgPointUtil::Pt(col, row); 00452 m_state.m_color[p] = SG_EMPTY; 00453 m_isBorder[p] = false; 00454 } 00455 m_state.m_all.Clear(); 00456 m_state.m_empty.Clear(); 00457 for (SgPoint p = 0; p < SG_MAXPOINT; ++p) 00458 { 00459 m_state.m_nuNeighbors[SG_BLACK][p] = 0; 00460 m_state.m_nuNeighbors[SG_WHITE][p] = 0; 00461 if (IsBorder(p)) 00462 m_state.m_nuNeighborsEmpty[p] = 4; 00463 else 00464 { 00465 m_state.m_empty.Include(p); 00466 m_state.m_nuNeighborsEmpty[p] = 0; 00467 for (SgNb4Iterator it(p); it; ++it) 00468 if (IsEmpty(*it)) 00469 ++m_state.m_nuNeighborsEmpty[p]; 00470 } 00471 } 00472 m_setup = setup; 00473 for (SgBWIterator c; c; ++c) 00474 for (SgSetIterator it(setup.m_stones[*c]); it; ++it) 00475 { 00476 SgPoint p = *it; 00477 SG_ASSERT(IsValidPoint(p)); 00478 SG_ASSERT(IsEmpty(p)); 00479 AddStone(p, *c); 00480 ++m_state.m_numStones[*c]; 00481 m_state.m_hash.XorStone(p, *c); 00482 m_state.m_isFirst[p] = false; 00483 } 00484 m_state.m_toPlay = setup.m_player; 00485 m_blockList->Clear(); 00486 m_state.m_block.Fill(0); 00487 for (GoBoard::Iterator it(*this); it; ++it) 00488 { 00489 SgBoardColor c = m_state.m_color[*it]; 00490 if (c != SG_EMPTY && m_state.m_block[*it] == 0) 00491 { 00492 GoBoard::Block& block = CreateNewBlock(); 00493 InitBlock(block, c, *it); 00494 } 00495 } 00496 m_snapshot->m_moveNumber = -1; 00497 CheckConsistency(); 00498 } 00499 00500 void GoBoard::InitBlock(GoBoard::Block& block, SgBlackWhite c, SgPoint anchor) 00501 { 00502 SG_ASSERT_BW(c); 00503 Block::LibertyList liberties; 00504 GoPointList stones; 00505 SgReserveMarker reserve(m_marker); 00506 m_marker.Clear(); 00507 SgStack<SgPoint,SG_MAX_ONBOARD> stack; 00508 stack.Push(anchor); 00509 m_marker.NewMark(anchor); 00510 while (! stack.IsEmpty()) 00511 { 00512 SgPoint p = stack.Pop(); 00513 if (m_state.m_color[p] == SG_EMPTY) 00514 { 00515 if (! liberties.Contains(p)) 00516 liberties.PushBack(p); 00517 } 00518 else if (m_state.m_color[p] == c) 00519 { 00520 stones.PushBack(p); 00521 m_state.m_block[p] = █ 00522 for (SgNb4Iterator it(p); it; ++it) 00523 if (! m_isBorder[*it] && m_marker.NewMark(*it)) 00524 stack.Push(*it); 00525 } 00526 } 00527 block.Init(c, anchor, stones, liberties); 00528 } 00529 00530 GoPlayerMove GoBoard::Move(int i) const 00531 { 00532 const StackEntry& entry = (*m_moves)[i]; 00533 SgPoint p = entry.m_point; 00534 SgBlackWhite c = entry.m_color; 00535 return GoPlayerMove(c, p); 00536 } 00537 00538 bool GoBoard::StackOverflowLikely() const 00539 { 00540 return (MoveNumber() > GO_MAX_NUM_MOVES - 50); 00541 } 00542 00543 int GoBoard::AdjacentBlocks(SgPoint point, int maxLib, SgPoint anchors[], 00544 int maxAnchors) const 00545 { 00546 SG_DEBUG_ONLY(maxAnchors); 00547 SG_ASSERT(Occupied(point)); 00548 const SgBlackWhite other = SgOppBW(GetStone(point)); 00549 int n = 0; 00550 SgReserveMarker reserve(m_marker); 00551 SG_UNUSED(reserve); 00552 m_marker.Clear(); 00553 for (GoBoard::StoneIterator it(*this, point); it; ++it) 00554 { 00555 if (NumNeighbors(*it, other) > 0) 00556 { 00557 SgPoint p = *it; 00558 if (IsColor(p - SG_NS, other) 00559 && m_marker.NewMark(Anchor(p - SG_NS)) 00560 && AtMostNumLibs(p - SG_NS, maxLib)) 00561 anchors[n++] = Anchor(p - SG_NS); 00562 if (IsColor(p - SG_WE, other) 00563 && m_marker.NewMark(Anchor(p - SG_WE)) 00564 && AtMostNumLibs(p - SG_WE, maxLib)) 00565 anchors[n++] = Anchor(p - SG_WE); 00566 if (IsColor(p + SG_WE, other) 00567 && m_marker.NewMark(Anchor(p + SG_WE)) 00568 && AtMostNumLibs(p + SG_WE, maxLib)) 00569 anchors[n++] = Anchor(p + SG_WE); 00570 if (IsColor(p + SG_NS, other) 00571 && m_marker.NewMark(Anchor(p + SG_NS)) 00572 && AtMostNumLibs(p + SG_NS, maxLib)) 00573 anchors[n++] = Anchor(p + SG_NS); 00574 } 00575 }; 00576 // Detect array overflow. 00577 SG_ASSERT(n < maxAnchors); 00578 anchors[n] = SG_ENDPOINT; 00579 return n; 00580 } 00581 00582 void GoBoard::NeighborBlocks(SgPoint p, SgBlackWhite c, 00583 SgPoint anchors[]) const 00584 { 00585 SG_ASSERT(IsEmpty(p)); 00586 SgReserveMarker reserve(m_marker); 00587 SG_UNUSED(reserve); 00588 m_marker.Clear(); 00589 int i = 0; 00590 if (NumNeighbors(p, c) > 0) 00591 { 00592 if (IsColor(p - SG_NS, c) && m_marker.NewMark(Anchor(p - SG_NS))) 00593 anchors[i++] = Anchor(p - SG_NS); 00594 if (IsColor(p - SG_WE, c) && m_marker.NewMark(Anchor(p - SG_WE))) 00595 anchors[i++] = Anchor(p - SG_WE); 00596 if (IsColor(p + SG_WE, c) && m_marker.NewMark(Anchor(p + SG_WE))) 00597 anchors[i++] = Anchor(p + SG_WE); 00598 if (IsColor(p + SG_NS, c) && m_marker.NewMark(Anchor(p + SG_NS))) 00599 anchors[i++] = Anchor(p + SG_NS); 00600 } 00601 anchors[i] = SG_ENDPOINT; 00602 } 00603 00604 void GoBoard::NeighborBlocks(SgPoint p, SgBlackWhite c, int maxLib, 00605 SgPoint anchors[]) const 00606 { 00607 SG_ASSERT(IsEmpty(p)); 00608 SgReserveMarker reserve(m_marker); 00609 SG_UNUSED(reserve); 00610 m_marker.Clear(); 00611 int i = 0; 00612 if (NumNeighbors(p, c) > 0) 00613 { 00614 if (IsColor(p - SG_NS, c) && m_marker.NewMark(Anchor(p - SG_NS)) 00615 && AtMostNumLibs(p - SG_NS, maxLib)) 00616 anchors[i++] = Anchor(p - SG_NS); 00617 if (IsColor(p - SG_WE, c) && m_marker.NewMark(Anchor(p - SG_WE)) 00618 && AtMostNumLibs(p - SG_WE, maxLib)) 00619 anchors[i++] = Anchor(p - SG_WE); 00620 if (IsColor(p + SG_WE, c) && m_marker.NewMark(Anchor(p + SG_WE)) 00621 && AtMostNumLibs(p + SG_WE, maxLib)) 00622 anchors[i++] = Anchor(p + SG_WE); 00623 if (IsColor(p + SG_NS, c) && m_marker.NewMark(Anchor(p + SG_NS)) 00624 && AtMostNumLibs(p + SG_NS, maxLib)) 00625 anchors[i++] = Anchor(p + SG_NS); 00626 } 00627 anchors[i] = SG_ENDPOINT; 00628 } 00629 00630 void GoBoard::AddStone(SgPoint p, SgBlackWhite c) 00631 { 00632 SG_ASSERT(IsEmpty(p)); 00633 SG_ASSERT_BW(c); 00634 m_state.m_color[p] = c; 00635 m_state.m_empty.Exclude(p); 00636 m_state.m_all[c].Include(p); 00637 --m_state.m_nuNeighborsEmpty[p - SG_NS]; 00638 --m_state.m_nuNeighborsEmpty[p - SG_WE]; 00639 --m_state.m_nuNeighborsEmpty[p + SG_WE]; 00640 --m_state.m_nuNeighborsEmpty[p + SG_NS]; 00641 SgArray<int,SG_MAXPOINT>& nuNeighbors = m_state.m_nuNeighbors[c]; 00642 ++nuNeighbors[p - SG_NS]; 00643 ++nuNeighbors[p - SG_WE]; 00644 ++nuNeighbors[p + SG_WE]; 00645 ++nuNeighbors[p + SG_NS]; 00646 } 00647 00648 void GoBoard::RemoveStone(SgPoint p) 00649 { 00650 SgBlackWhite c = GetStone(p); 00651 SG_ASSERT_BW(c); 00652 m_state.m_color[p] = SG_EMPTY; 00653 m_state.m_empty.Include(p); 00654 m_state.m_all[c].Exclude(p); 00655 ++m_state.m_nuNeighborsEmpty[p - SG_NS]; 00656 ++m_state.m_nuNeighborsEmpty[p - SG_WE]; 00657 ++m_state.m_nuNeighborsEmpty[p + SG_WE]; 00658 ++m_state.m_nuNeighborsEmpty[p + SG_NS]; 00659 SgArray<int,SG_MAXPOINT>& nuNeighbors = m_state.m_nuNeighbors[c]; 00660 --nuNeighbors[p - SG_NS]; 00661 --nuNeighbors[p - SG_WE]; 00662 --nuNeighbors[p + SG_WE]; 00663 --nuNeighbors[p + SG_NS]; 00664 } 00665 00666 void GoBoard::KillBlock(const Block* block) 00667 { 00668 SgBlackWhite c = block->Color(); 00669 SgBlackWhite opp = SgOppBW(c); 00670 for (Block::StoneIterator it(block->Stones()); it; ++it) 00671 { 00672 SgPoint stn = *it; 00673 AddLibToAdjBlocks(stn, opp); 00674 m_state.m_hash.XorStone(stn, c); 00675 RemoveStone(stn); 00676 m_capturedStones.PushBack(stn); 00677 m_state.m_block[stn] = 0; 00678 } 00679 int nuStones = block->Stones().Length(); 00680 m_state.m_numStones[c] -= nuStones; 00681 m_state.m_prisoners[c] += nuStones; 00682 if (nuStones == 1) 00683 // Remember that single stone was captured, check conditions on 00684 // capturing block later 00685 m_state.m_koPoint = block->Anchor(); 00686 } 00687 00688 bool GoBoard::FullBoardRepetition() const 00689 { 00690 GoRules::KoRule koRule = Rules().GetKoRule(); 00691 if (koRule == GoRules::SIMPLEKO) 00692 { 00693 int nuMoves = MoveNumber(); 00694 if (nuMoves == 0) 00695 return false; 00696 const StackEntry& entry = (*m_moves)[nuMoves - 1]; 00697 return (entry.m_point == entry.m_koPoint); 00698 } 00699 SgBWArray<SgArrayList<SgPoint,SG_MAXPOINT> > changes; 00700 int nuChanges = 0; 00701 int moveNumber = m_moves->Length() - 1; 00702 bool requireSameToPlay = (koRule == GoRules::SUPERKO); 00703 while (moveNumber >= 0) 00704 { 00705 const StackEntry& entry = (*m_moves)[moveNumber]; 00706 SgPoint p = entry.m_point; 00707 if (! IsPass(p) && entry.m_color != SG_EMPTY 00708 && entry.m_color == GetColor(p) 00709 && entry.m_isFirst) 00710 // In this case we know that all erlier positions are 00711 // empty at p, therefore different to current position 00712 return false; 00713 00714 if (! IsPass(entry.m_point)) 00715 { 00716 UpdateChanges(entry.m_point, changes[entry.m_color], nuChanges); 00717 for (SgArrayList<Block*,4>::Iterator it(entry.m_killed); it; ++it) 00718 { 00719 Block* killed = *it; 00720 for (GoPointList::Iterator stn(killed->Stones()); stn; ++stn) 00721 UpdateChanges(*stn, changes[killed->Color()], nuChanges); 00722 } 00723 Block* suicide = entry.m_suicide; 00724 if (suicide != 0) 00725 for (GoPointList::Iterator stn(suicide->Stones()); stn; 00726 ++stn) 00727 UpdateChanges(*stn, changes[suicide->Color()], nuChanges); 00728 } 00729 00730 if (nuChanges > 0 00731 && (! requireSameToPlay || entry.m_toPlay == m_state.m_toPlay) 00732 && changes[SG_BLACK].IsEmpty() && changes[SG_WHITE].IsEmpty()) 00733 return true; 00734 --moveNumber; 00735 } 00736 return false; 00737 } 00738 00739 bool GoBoard::CheckSuicide(SgPoint p, StackEntry& entry) 00740 { 00741 if (! HasLiberties(p)) 00742 { 00743 entry.m_suicide = m_state.m_block[p]; 00744 KillBlock(entry.m_suicide); 00745 m_moveInfo.set(GO_MOVEFLAG_SUICIDE); 00746 return m_rules.AllowSuicide(); 00747 } 00748 return true; 00749 } 00750 00751 void GoBoard::Play(SgPoint p, SgBlackWhite player) 00752 { 00753 SG_ASSERT(p != SG_NULLMOVE); 00754 SG_ASSERT_BW(player); 00755 SG_ASSERT(IsPass(p) || (IsValidPoint(p) && IsEmpty(p))); 00756 CheckConsistency(); 00757 ++m_countPlay; 00758 // Reuse stack entry without initialization 00759 m_moves->Resize(m_moves->Length() + 1); 00760 StackEntry& entry = m_moves->Last(); 00761 entry.m_point = p; 00762 entry.m_color = player; 00763 SaveState(entry); 00764 m_state.m_koPoint = SG_NULLPOINT; 00765 m_capturedStones.Clear(); 00766 m_moveInfo.reset(); 00767 SgBlackWhite opp = SgOppBW(player); 00768 if (IsPass(p)) 00769 { 00770 m_state.m_toPlay = opp; 00771 return; 00772 } 00773 bool isLegal = true; 00774 // Place the stone there tentatively. Remember whether this is the 00775 // first time a stone gets played at that point to speed up check 00776 // for full-board repetition below. 00777 bool wasFirstStone = IsFirst(p); 00778 m_state.m_isFirst[p] = false; 00779 m_state.m_hash.XorStone(p, player); 00780 AddStone(p, player); 00781 ++m_state.m_numStones[player]; 00782 RemoveLibAndKill(p, opp, entry); 00783 if (! entry.m_killed.IsEmpty()) 00784 { 00785 m_moveInfo.set(GO_MOVEFLAG_CAPTURING); 00786 // If this is the first time a point is played here, then repetition 00787 // is impossible until the next capture 00788 m_state.m_isNewPosition = m_state.m_isNewPosition && wasFirstStone; 00789 } 00790 UpdateBlocksAfterAddStone(p, player, entry); 00791 entry.m_suicide = 0; 00792 if (m_state.m_koPoint != SG_NULLPOINT) 00793 if (NumStones(p) > 1 || NumLiberties(p) > 1) 00794 m_state.m_koPoint = SG_NULLPOINT; 00795 isLegal = CheckSuicide(p, entry); 00796 m_state.m_toPlay = opp; 00797 if (! wasFirstStone && ! IsNewPosition() && ! CheckKo(player)) 00798 isLegal = false; 00799 if (! isLegal) 00800 m_moveInfo.set(GO_MOVEFLAG_ILLEGAL); 00801 // @see @ref sgboardhashhistory 00802 if (! m_capturedStones.IsEmpty() && m_koModifiesHash) 00803 { 00804 // This assumes that in the exact same position, the stones will 00805 // be captured in the same sequence. Currently holds due to the 00806 // way KillBlockIfNoLiberty is implemented; may be fragile. 00807 SgPoint firstCapturedStone = m_capturedStones[0]; 00808 m_state.m_hash.XorCaptured(MoveNumber(), firstCapturedStone); 00809 } 00810 CheckConsistency(); 00811 } 00812 00813 void GoBoard::Undo() 00814 { 00815 CheckConsistency(); 00816 const StackEntry& entry = m_moves->Last(); 00817 RestoreState(entry); 00818 UpdateBlocksAfterUndo(entry); 00819 m_moves->PopBack(); 00820 CheckConsistency(); 00821 } 00822 00823 /** Remove liberty from adjacent blocks and kill opponent blocks without 00824 liberties. */ 00825 void GoBoard::RemoveLibAndKill(SgPoint p, SgBlackWhite opp, 00826 StackEntry& entry) 00827 { 00828 entry.m_killed.Clear(); 00829 if (NumNeighbors(p, SG_BLACK) == 0 && NumNeighbors(p, SG_WHITE) == 0) 00830 return; 00831 SgArrayList<Block*,4> blocks = GetAdjacentBlocks(p); 00832 for (SgArrayList<Block*,4>::Iterator it(blocks); it; ++it) 00833 { 00834 Block* b = *it; 00835 b->ExcludeLiberty(p); 00836 if (b->Color() == opp && b->NumLiberties() == 0) 00837 { 00838 entry.m_killed.PushBack(b); 00839 KillBlock(b); 00840 } 00841 } 00842 } 00843 00844 void GoBoard::RestoreState(const StackEntry& entry) 00845 { 00846 m_state.m_hash = entry.m_hash; 00847 m_state.m_koPoint = entry.m_koPoint; 00848 if (! IsPass(entry.m_point)) 00849 { 00850 m_state.m_isFirst[entry.m_point] = entry.m_isFirst; 00851 m_state.m_isNewPosition = entry.m_isNewPosition; 00852 } 00853 m_state.m_toPlay = entry.m_toPlay; 00854 m_state.m_koLevel = entry.m_koLevel; 00855 m_koColor = entry.m_koColor; 00856 m_koLoser = entry.m_koLoser; 00857 m_koModifiesHash = entry.m_koModifiesHash; 00858 } 00859 00860 void GoBoard::SaveState(StackEntry& entry) 00861 { 00862 entry.m_hash = m_state.m_hash; 00863 if (! IsPass(entry.m_point)) 00864 { 00865 entry.m_isFirst = m_state.m_isFirst[entry.m_point]; 00866 entry.m_isNewPosition = m_state.m_isNewPosition; 00867 } 00868 entry.m_toPlay = m_state.m_toPlay; 00869 entry.m_koPoint = m_state.m_koPoint; 00870 entry.m_koLevel = m_state.m_koLevel; 00871 entry.m_koColor = m_koColor; 00872 entry.m_koLoser = m_koLoser; 00873 entry.m_koModifiesHash = m_koModifiesHash; 00874 } 00875 00876 void GoBoard::TakeSnapshot() 00877 { 00878 m_snapshot->m_moveNumber = MoveNumber(); 00879 m_snapshot->m_blockListSize = m_blockList->Length(); 00880 m_snapshot->m_state = m_state; 00881 for (GoBoard::Iterator it(*this); it; ++it) 00882 { 00883 SgPoint p = *it; 00884 if (m_state.m_block[p] != 0) 00885 m_snapshot->m_blockArray[p] = *m_state.m_block[p]; 00886 } 00887 } 00888 00889 void GoBoard::RestoreSnapshot() 00890 { 00891 SG_ASSERT(m_snapshot->m_moveNumber >= 0); 00892 SG_ASSERT(m_snapshot->m_moveNumber <= MoveNumber()); 00893 if (m_snapshot->m_moveNumber == MoveNumber()) 00894 return; 00895 m_blockList->Resize(m_snapshot->m_blockListSize); 00896 m_moves->Resize(m_snapshot->m_moveNumber); 00897 m_state = m_snapshot->m_state; 00898 for (GoBoard::Iterator it(*this); it; ++it) 00899 { 00900 SgPoint p = *it; 00901 if (m_state.m_block[p] != 0) 00902 *m_state.m_block[p] = m_snapshot->m_blockArray[p]; 00903 } 00904 CheckConsistency(); 00905 } 00906 00907 //----------------------------------------------------------------------------