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


17 Jun 2010 Doxygen 1.4.7