00001 //---------------------------------------------------------------------------- 00002 /** @file GoBoard.cpp 00003 See GoBoard.h */ 00004 //---------------------------------------------------------------------------- 00005 00006 #include "SgSystem.h" 00007 #include "GoUctBoard.h" 00008 00009 #include <boost/static_assert.hpp> 00010 #include <algorithm> 00011 #include "GoBoardUtil.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 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 } // namespace 00029 00030 //---------------------------------------------------------------------------- 00031 00032 GoUctBoard::GoUctBoard(const GoBoard& bd) 00033 : m_const(bd.Size()) 00034 { 00035 m_size = -1; 00036 Init(bd); 00037 } 00038 00039 GoUctBoard::~GoUctBoard() 00040 { 00041 } 00042 00043 void GoUctBoard::CheckConsistency() const 00044 { 00045 if (! CONSISTENCY) 00046 return; 00047 for (SgPoint p = 0; p < SG_MAXPOINT; ++p) 00048 { 00049 if (IsBorder(p)) 00050 continue; 00051 int c = m_color[p]; 00052 SG_ASSERT_EBW(c); 00053 int n = 0; 00054 for (SgNb4Iterator it(p); it; ++it) 00055 if (m_color[*it] == SG_EMPTY) 00056 ++n; 00057 SG_ASSERT(n == NumEmptyNeighbors(p)); 00058 n = 0; 00059 for (SgNb4Iterator it(p); it; ++it) 00060 if (m_color[*it] == SG_BLACK) 00061 ++n; 00062 SG_ASSERT(n == NumNeighbors(p, SG_BLACK)); 00063 n = 0; 00064 for (SgNb4Iterator it(p); it; ++it) 00065 if (m_color[*it] == SG_WHITE) 00066 ++n; 00067 SG_ASSERT(n == NumNeighbors(p, SG_WHITE)); 00068 if (c == SG_BLACK || c == SG_WHITE) 00069 CheckConsistencyBlock(p); 00070 if (c == SG_EMPTY) 00071 SG_ASSERT(m_block[p] == 0); 00072 } 00073 } 00074 00075 void GoUctBoard::CheckConsistencyBlock(SgPoint point) const 00076 { 00077 SG_ASSERT(Occupied(point)); 00078 SgBlackWhite color = GetColor(point); 00079 GoPointList stones; 00080 Block::LibertyList liberties; 00081 SgMarker mark; 00082 SgStack<SgPoint,SG_MAXPOINT> stack; 00083 stack.Push(point); 00084 bool anchorFound = false; 00085 const Block* block = m_block[point]; 00086 while (! stack.IsEmpty()) 00087 { 00088 SgPoint p = stack.Pop(); 00089 if (IsBorder(p) || ! mark.NewMark(p)) 00090 continue; 00091 if (GetColor(p) == color) 00092 { 00093 stones.PushBack(p); 00094 if (p == block->m_anchor) 00095 anchorFound = true; 00096 stack.Push(p - SG_NS); 00097 stack.Push(p - SG_WE); 00098 stack.Push(p + SG_WE); 00099 stack.Push(p + SG_NS); 00100 } 00101 else if (GetColor(p) == SG_EMPTY) 00102 liberties.PushBack(p); 00103 } 00104 SG_ASSERT(anchorFound); 00105 SG_ASSERT(color == block->m_color); 00106 SG_ASSERT(stones.SameElements(block->m_stones)); 00107 SG_ASSERT(liberties.SameElements(block->m_liberties)); 00108 SG_ASSERT(stones.Length() == NumStones(point)); 00109 } 00110 00111 void GoUctBoard::AddLibToAdjBlocks(SgPoint p, SgBlackWhite c) 00112 { 00113 if (NumNeighbors(p, c) == 0) 00114 return; 00115 SgReserveMarker reserve(m_marker2); 00116 m_marker2.Clear(); 00117 Block* b; 00118 if (m_color[p - SG_NS] == c && (b = m_block[p - SG_NS]) != 0) 00119 { 00120 m_marker2.Include(b->m_anchor); 00121 b->m_liberties.PushBack(p); 00122 } 00123 if (m_color[p + SG_NS] == c && (b = m_block[p + SG_NS]) != 0 00124 && m_marker2.NewMark(b->m_anchor)) 00125 b->m_liberties.PushBack(p); 00126 if (m_color[p - SG_WE] == c && (b = m_block[p - SG_WE]) != 0 00127 && m_marker2.NewMark(b->m_anchor)) 00128 b->m_liberties.PushBack(p); 00129 if (m_color[p + SG_WE] == c && (b = m_block[p + SG_WE]) != 0 00130 && ! m_marker2.Contains(b->m_anchor)) 00131 b->m_liberties.PushBack(p); 00132 } 00133 00134 void GoUctBoard::AddStoneToBlock(SgPoint p, Block* block) 00135 { 00136 // Stone already placed 00137 SG_ASSERT(IsColor(p, block->m_color)); 00138 block->m_stones.PushBack(p); 00139 if (IsEmpty(p - SG_NS) && ! IsAdjacentTo(p - SG_NS, block)) 00140 block->m_liberties.PushBack(p - SG_NS); 00141 if (IsEmpty(p - SG_WE) && ! IsAdjacentTo(p - SG_WE, block)) 00142 block->m_liberties.PushBack(p - SG_WE); 00143 if (IsEmpty(p + SG_WE) && ! IsAdjacentTo(p + SG_WE, block)) 00144 block->m_liberties.PushBack(p + SG_WE); 00145 if (IsEmpty(p + SG_NS) && ! IsAdjacentTo(p + SG_NS, block)) 00146 block->m_liberties.PushBack(p + SG_NS); 00147 m_block[p] = block; 00148 } 00149 00150 void GoUctBoard::CreateSingleStoneBlock(SgPoint p, SgBlackWhite c) 00151 { 00152 // Stone already placed 00153 SG_ASSERT(IsColor(p, c)); 00154 SG_ASSERT(NumNeighbors(p, c) == 0); 00155 Block& block = m_blockArray[p]; 00156 block.InitSingleStoneBlock(c, p); 00157 if (IsEmpty(p - SG_NS)) 00158 block.m_liberties.PushBack(p - SG_NS); 00159 if (IsEmpty(p - SG_WE)) 00160 block.m_liberties.PushBack(p - SG_WE); 00161 if (IsEmpty(p + SG_WE)) 00162 block.m_liberties.PushBack(p + SG_WE); 00163 if (IsEmpty(p + SG_NS)) 00164 block.m_liberties.PushBack(p + SG_NS); 00165 m_block[p] = █ 00166 } 00167 00168 bool GoUctBoard::IsAdjacentTo(SgPoint p, 00169 const GoUctBoard::Block* block) const 00170 { 00171 return m_block[p - SG_NS] == block 00172 || m_block[p - SG_WE] == block 00173 || m_block[p + SG_WE] == block 00174 || m_block[p + SG_NS] == block; 00175 } 00176 00177 void GoUctBoard::MergeBlocks(SgPoint p, const SgArrayList<Block*,4>& adjBlocks) 00178 { 00179 // Stone already placed 00180 SG_ASSERT(IsColor(p, adjBlocks[0]->m_color)); 00181 SG_ASSERT(NumNeighbors(p, adjBlocks[0]->m_color) > 1); 00182 Block* largestBlock = 0; 00183 int largestBlockStones = 0; 00184 for (SgArrayList<Block*,4>::Iterator it(adjBlocks); it; ++it) 00185 { 00186 Block* adjBlock = *it; 00187 int numStones = adjBlock->m_stones.Length(); 00188 if (numStones > largestBlockStones) 00189 { 00190 largestBlockStones = numStones; 00191 largestBlock = adjBlock; 00192 } 00193 } 00194 largestBlock->m_stones.PushBack(p); 00195 SgReserveMarker reserve(m_marker); 00196 m_marker.Clear(); 00197 for (Block::LibertyIterator lib(largestBlock->m_liberties); lib; ++lib) 00198 m_marker.Include(*lib); 00199 for (SgArrayList<Block*,4>::Iterator it(adjBlocks); it; ++it) 00200 { 00201 Block* adjBlock = *it; 00202 if (adjBlock == largestBlock) 00203 continue; 00204 for (Block::StoneIterator stn(adjBlock->m_stones); stn; ++stn) 00205 { 00206 largestBlock->m_stones.PushBack(*stn); 00207 m_block[*stn] = largestBlock; 00208 } 00209 for (Block::LibertyIterator lib(adjBlock->m_liberties); lib; ++lib) 00210 if (m_marker.NewMark(*lib)) 00211 largestBlock->m_liberties.PushBack(*lib); 00212 } 00213 m_block[p] = largestBlock; 00214 if (IsEmpty(p - SG_NS) && m_marker.NewMark(p - SG_NS)) 00215 largestBlock->m_liberties.PushBack(p - SG_NS); 00216 if (IsEmpty(p - SG_WE) && m_marker.NewMark(p - SG_WE)) 00217 largestBlock->m_liberties.PushBack(p - SG_WE); 00218 if (IsEmpty(p + SG_WE) && m_marker.NewMark(p + SG_WE)) 00219 largestBlock->m_liberties.PushBack(p + SG_WE); 00220 if (IsEmpty(p + SG_NS) && m_marker.NewMark(p + SG_NS)) 00221 largestBlock->m_liberties.PushBack(p + SG_NS); 00222 } 00223 00224 void GoUctBoard::UpdateBlocksAfterAddStone(SgPoint p, SgBlackWhite c, 00225 const SgArrayList<Block*,4>& adjBlocks) 00226 { 00227 // Stone already placed 00228 SG_ASSERT(IsColor(p, c)); 00229 int n = adjBlocks.Length(); 00230 if (n == 0) 00231 CreateSingleStoneBlock(p, c); 00232 else 00233 { 00234 if (n == 1) 00235 AddStoneToBlock(p, adjBlocks[0]); 00236 else 00237 MergeBlocks(p, adjBlocks); 00238 } 00239 } 00240 00241 void GoUctBoard::Init(const GoBoard& bd) 00242 { 00243 if (bd.Size() != m_size) 00244 InitSize(bd); 00245 m_prisoners[SG_BLACK] = bd.NumPrisoners(SG_BLACK); 00246 m_prisoners[SG_WHITE] = bd.NumPrisoners(SG_WHITE); 00247 m_koPoint = bd.KoPoint(); 00248 m_lastMove = bd.GetLastMove(); 00249 m_secondLastMove = bd.Get2ndLastMove(); 00250 m_toPlay = bd.ToPlay(); 00251 for (GoBoard::Iterator it(bd); it; ++it) 00252 { 00253 SgPoint p = *it; 00254 SgBoardColor c = bd.GetColor(p); 00255 m_color[p] = c; 00256 m_nuNeighbors[SG_BLACK][p] = bd.NumNeighbors(p, SG_BLACK); 00257 m_nuNeighbors[SG_WHITE][p] = bd.NumNeighbors(p, SG_WHITE); 00258 m_nuNeighborsEmpty[p] = bd.NumEmptyNeighbors(p); 00259 if (bd.IsEmpty(p)) 00260 m_block[p] = 0; 00261 else if (bd.Anchor(p) == p) 00262 { 00263 SgBoardColor c = m_color[p]; 00264 Block& block = m_blockArray[p]; 00265 block.InitNewBlock(c, p); 00266 for (GoBoard::StoneIterator it2(bd, p); it2; ++it2) 00267 { 00268 block.m_stones.PushBack(*it2); 00269 m_block[*it2] = █ 00270 } 00271 for (GoBoard::LibertyIterator it2(bd, p); it2; ++it2) 00272 block.m_liberties.PushBack(*it2); 00273 } 00274 } 00275 CheckConsistency(); 00276 } 00277 00278 void GoUctBoard::InitSize(const GoBoard& bd) 00279 { 00280 m_size = bd.Size(); 00281 m_nuNeighbors[SG_BLACK].Fill(0); 00282 m_nuNeighbors[SG_WHITE].Fill(0); 00283 m_nuNeighborsEmpty.Fill(0); 00284 m_block.Fill(0); 00285 for (SgPoint p = 0; p < SG_MAXPOINT; ++p) 00286 { 00287 if (bd.IsBorder(p)) 00288 { 00289 m_color[p] = SG_BORDER; 00290 m_isBorder[p] = true; 00291 } 00292 else 00293 m_isBorder[p] = false; 00294 } 00295 m_const.ChangeSize(m_size); 00296 } 00297 00298 void GoUctBoard::NeighborBlocks(SgPoint p, SgBlackWhite c, 00299 SgPoint anchors[]) const 00300 { 00301 SG_ASSERT(IsEmpty(p)); 00302 SgReserveMarker reserve(m_marker); 00303 SG_UNUSED(reserve); 00304 m_marker.Clear(); 00305 int i = 0; 00306 if (NumNeighbors(p, c) > 0) 00307 { 00308 if (IsColor(p - SG_NS, c) && m_marker.NewMark(Anchor(p - SG_NS))) 00309 anchors[i++] = Anchor(p - SG_NS); 00310 if (IsColor(p - SG_WE, c) && m_marker.NewMark(Anchor(p - SG_WE))) 00311 anchors[i++] = Anchor(p - SG_WE); 00312 if (IsColor(p + SG_WE, c) && m_marker.NewMark(Anchor(p + SG_WE))) 00313 anchors[i++] = Anchor(p + SG_WE); 00314 if (IsColor(p + SG_NS, c) && m_marker.NewMark(Anchor(p + SG_NS))) 00315 anchors[i++] = Anchor(p + SG_NS); 00316 } 00317 anchors[i] = SG_ENDPOINT; 00318 } 00319 00320 void GoUctBoard::AddStone(SgPoint p, SgBlackWhite c) 00321 { 00322 SG_ASSERT(IsEmpty(p)); 00323 SG_ASSERT_BW(c); 00324 m_color[p] = c; 00325 --m_nuNeighborsEmpty[p - SG_NS]; 00326 --m_nuNeighborsEmpty[p - SG_WE]; 00327 --m_nuNeighborsEmpty[p + SG_WE]; 00328 --m_nuNeighborsEmpty[p + SG_NS]; 00329 SgArray<int,SG_MAXPOINT>& nuNeighbors = m_nuNeighbors[c]; 00330 ++nuNeighbors[p - SG_NS]; 00331 ++nuNeighbors[p - SG_WE]; 00332 ++nuNeighbors[p + SG_WE]; 00333 ++nuNeighbors[p + SG_NS]; 00334 } 00335 00336 /** Remove liberty from adjacent blocks and kill opponent blocks without 00337 liberties. 00338 As a side effect, computes adjacent blocks of own color to avoid a 00339 second call to GetAdjacentBlocks() in UpdateBlocksAfterAddStone(). */ 00340 void GoUctBoard::RemoveLibAndKill(SgPoint p, SgBlackWhite opp, 00341 SgArrayList<Block*,4>& ownAdjBlocks) 00342 { 00343 SgReserveMarker reserve(m_marker); 00344 m_marker.Clear(); 00345 Block* b; 00346 if ((b = m_block[p - SG_NS]) != 0) 00347 { 00348 m_marker.Include(b->m_anchor); 00349 b->m_liberties.Exclude(p); 00350 if (b->m_color == opp) 00351 { 00352 if (b->m_liberties.Length() == 0) 00353 KillBlock(b); 00354 } 00355 else 00356 ownAdjBlocks.PushBack(b); 00357 } 00358 if ((b = m_block[p - SG_WE]) != 0 && m_marker.NewMark(b->m_anchor)) 00359 { 00360 b->m_liberties.Exclude(p); 00361 if (b->m_color == opp) 00362 { 00363 if (b->m_liberties.Length() == 0) 00364 KillBlock(b); 00365 } 00366 else 00367 ownAdjBlocks.PushBack(b); 00368 } 00369 if ((b = m_block[p + SG_WE]) != 0 && m_marker.NewMark(b->m_anchor)) 00370 { 00371 b->m_liberties.Exclude(p); 00372 if (b->m_color == opp) 00373 { 00374 if (b->m_liberties.Length() == 0) 00375 KillBlock(b); 00376 } 00377 else 00378 ownAdjBlocks.PushBack(b); 00379 } 00380 if ((b = m_block[p + SG_NS]) != 0 && ! m_marker.Contains(b->m_anchor)) 00381 { 00382 b->m_liberties.Exclude(p); 00383 if (b->m_color == opp) 00384 { 00385 if (b->m_liberties.Length() == 0) 00386 KillBlock(b); 00387 } 00388 else 00389 ownAdjBlocks.PushBack(b); 00390 } 00391 } 00392 00393 void GoUctBoard::KillBlock(const Block* block) 00394 { 00395 SgBlackWhite c = block->m_color; 00396 SgBlackWhite opp = SgOppBW(c); 00397 SgArray<int,SG_MAXPOINT>& nuNeighbors = m_nuNeighbors[c]; 00398 for (Block::StoneIterator it(block->m_stones); it; ++it) 00399 { 00400 SgPoint p = *it; 00401 AddLibToAdjBlocks(p, opp); 00402 m_color[p] = SG_EMPTY; 00403 ++m_nuNeighborsEmpty[p - SG_NS]; 00404 ++m_nuNeighborsEmpty[p - SG_WE]; 00405 ++m_nuNeighborsEmpty[p + SG_WE]; 00406 ++m_nuNeighborsEmpty[p + SG_NS]; 00407 --nuNeighbors[p - SG_NS]; 00408 --nuNeighbors[p - SG_WE]; 00409 --nuNeighbors[p + SG_WE]; 00410 --nuNeighbors[p + SG_NS]; 00411 m_capturedStones.PushBack(p); 00412 m_block[p] = 0; 00413 } 00414 int nuStones = block->m_stones.Length(); 00415 m_prisoners[c] += nuStones; 00416 if (nuStones == 1) 00417 // Remember that single stone was captured, check conditions on 00418 // capturing block later 00419 m_koPoint = block->m_anchor; 00420 } 00421 00422 void GoUctBoard::Play(SgPoint p) 00423 { 00424 SG_ASSERT(p >= 0); // No special move, see SgMove 00425 SG_ASSERT(p == SG_PASS || (IsValidPoint(p) && IsEmpty(p))); 00426 CheckConsistency(); 00427 m_koPoint = SG_NULLPOINT; 00428 m_capturedStones.Clear(); 00429 SgBlackWhite opp = SgOppBW(m_toPlay); 00430 if (p != SG_PASS) 00431 { 00432 AddStone(p, m_toPlay); 00433 SgArrayList<Block*,4> adjBlocks; 00434 if (NumNeighbors(p, SG_BLACK) > 0 || NumNeighbors(p, SG_WHITE) > 0) 00435 RemoveLibAndKill(p, opp, adjBlocks); 00436 UpdateBlocksAfterAddStone(p, m_toPlay, adjBlocks); 00437 if (m_koPoint != SG_NULLPOINT) 00438 if (NumStones(p) > 1 || NumLiberties(p) > 1) 00439 m_koPoint = SG_NULLPOINT; 00440 SG_ASSERT(HasLiberties(p)); // Suicide not supported by GoUctBoard 00441 } 00442 m_secondLastMove = m_lastMove; 00443 m_lastMove = p; 00444 m_toPlay = opp; 00445 CheckConsistency(); 00446 } 00447 00448 //----------------------------------------------------------------------------