00001
00002
00003
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
00022
00023
00024
00025
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 }
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
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
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
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
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
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
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
00684
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
00711
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
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
00775
00776
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
00787
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
00802 if (! m_capturedStones.IsEmpty() && m_koModifiesHash)
00803 {
00804
00805
00806
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
00824
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