00001
00002
00003
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
00023
00024
00025
00026
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 }
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
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
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
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
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
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
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
00674
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
00704
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
00731
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
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
00795
00796
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
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
00819 if (! m_capturedStones.IsEmpty() && m_koModifiesHash)
00820 {
00821
00822
00823
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
00841
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