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