Index   Main   Namespaces   Classes   Hierarchy   Annotated   Files   Compound   Global   Pages  

GoBoard.cpp

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


Sun Mar 13 2011 Doxygen 1.7.1