Index   Main   Namespaces   Classes   Hierarchy   Annotated   Files   Compound   Global   Pages  

GoUctPatterns.h

Go to the documentation of this file.
00001 //----------------------------------------------------------------------------
00002 /** @file GoUctPatterns.h */
00003 //----------------------------------------------------------------------------
00004 
00005 #ifndef GOUCT_PATTERNS_H
00006 #define GOUCT_PATTERNS_H
00007 
00008 #include "GoBoard.h"
00009 #include "GoBoardUtil.h"
00010 #include "SgBoardColor.h"
00011 #include "SgBWArray.h"
00012 #include "SgPoint.h"
00013 
00014 //----------------------------------------------------------------------------
00015 
00016 /** Some hard-coded pattern matching routines to match patterns used by MoGo.
00017     See <a href="http://hal.inria.fr/docs/00/11/72/66/PDF/MoGoReport.pdf">
00018     Modification of UCT with Patterns in Monte-Carlo Go</a>.
00019 
00020     The move is always in the center of the pattern or at the middle edge
00021     point (lower line) for edge patterns. The patterns are matched for both
00022     colors, unless specified otherwise. Notation:
00023     @verbatim
00024     O  White            x = Black or Empty
00025     X = Black           o = White or Empty
00026     . = Empty           B = Black to Play
00027     ? = Don't care      W = White to Play
00028     @endverbatim
00029 
00030     Patterns for Hane. <br>
00031     True is returned if any pattern is matched.
00032     @verbatim
00033     X O X   X O .   X O ?   X O O
00034     . . .   . . .   X . .   . . .
00035     ? ? ?   ? . ?   ? . ?   ? . ? B
00036     @endverbatim
00037 
00038     Patterns for Cut1. <br>
00039     True is returned if the first pattern is matched, but not the next two.
00040     @verbatim
00041     X O ?   X O ?   X O ?
00042     O . ?   O . O   O . .
00043     ? ? ?   ? . ?   ? O ?
00044     @endverbatim
00045 
00046     Pattern for Cut2.
00047     @verbatim
00048     ? X ?
00049     O . O
00050     x x x
00051     @endverbatim
00052 
00053     Pattern for Edge. <br>
00054     True is returned if any pattern is matched.
00055     @verbatim
00056     X . ?   ? X ?   ? X O    ? X O    ? X O
00057     O . ?   o . O   ? . ? B  ? . o W  O . X W
00058     @endverbatim */
00059 template<class BOARD>
00060 class GoUctPatterns
00061 {
00062 public:
00063     GoUctPatterns(const BOARD& bd);
00064 
00065     /** Match any of the patterns. */
00066     bool MatchAny(SgPoint p) const;
00067 
00068 private:
00069     /** 3^5 = size of edge pattern table */
00070     static const int GOUCT_POWER3_5 = 3 * 3 * 3 * 3 * 3;
00071 
00072     /** 3^8 = size of center pattern table. */
00073     static const int GOUCT_POWER3_8 = 3 * 3 * 3 * 3 * 3 * 3 * 3 * 3;
00074 
00075     /** See m_edgeTable. */
00076     typedef SgArray<bool, GOUCT_POWER3_5> GoUctEdgePatternTable;
00077 
00078     /** See m_table. */
00079     typedef SgArray<bool, GOUCT_POWER3_8> GoUctPatternTable;
00080 
00081     const BOARD& m_bd;
00082 
00083     /** lookup table for 8-neighborhood of a move candidate */
00084     SgBWArray<GoUctPatternTable> m_table;
00085 
00086     /** lookup table on the edge of board */
00087     SgBWArray<GoUctEdgePatternTable> m_edgeTable;
00088 
00089     static bool CheckCut1(const GoBoard& bd, SgPoint p, SgBlackWhite c,
00090                           int cDir, int otherDir);
00091 
00092     static bool CheckCut2(const GoBoard& bd, SgPoint p, const SgBlackWhite c,
00093                           int cDir, int otherDir);
00094 
00095     static bool CheckHane1(const GoBoard& bd, SgPoint p, SgBlackWhite c,
00096                            SgBlackWhite opp, int cDir, int otherDir);
00097 
00098     static int CodeOf8Neighbors(const BOARD& bd, SgPoint p);
00099 
00100     static int CodeOfEdgeNeighbors(const BOARD& bd, SgPoint p);
00101 
00102     static int EdgeDirection(GoBoard& bd, SgPoint p, int index);
00103 
00104     static int EBWCodeOfPoint(const BOARD& bd, SgPoint p);
00105 
00106     static int FindDir(const GoBoard& bd, SgPoint p, SgBlackWhite c);
00107 
00108     static void InitCenterPatternTable(SgBWArray<GoUctPatternTable>& table);
00109 
00110     static void InitEdgePatternTable(SgBWArray<GoUctEdgePatternTable>&
00111                                      edgeTable);
00112 
00113     static bool MatchCut(const GoBoard& bd, SgPoint p);
00114 
00115     static bool MatchEdge(const GoBoard& bd, SgPoint p, const int nuBlack,
00116                           const int nuWhite);
00117 
00118     static bool MatchHane(const GoBoard& bd, SgPoint p, const int nuBlack,
00119                           const int nuWhite);
00120 
00121     static bool MatchAnyPattern(const GoBoard& bd, SgPoint p);
00122 
00123     static int OtherDir(int dir);
00124 
00125     static int SetupCodedEdgePosition(GoBoard& bd, int code);
00126 
00127     static int SetupCodedPosition(GoBoard& bd, int code);
00128 
00129     /** Match any of the center patterns. */
00130     bool MatchAnyCenter(SgPoint p) const;
00131 
00132     /** Match any of the edge patterns. */
00133     bool MatchAnyEdge(SgPoint p) const;
00134 };
00135 
00136 template<class BOARD>
00137 GoUctPatterns<BOARD>::GoUctPatterns(const BOARD& bd)
00138     : m_bd(bd)
00139 {
00140     InitCenterPatternTable(m_table);
00141     InitEdgePatternTable(m_edgeTable);
00142 }
00143 
00144 template<class BOARD>
00145 bool GoUctPatterns<BOARD>::CheckHane1(const GoBoard& bd, SgPoint p,
00146                                       SgBlackWhite c, SgBlackWhite opp,
00147                                       int cDir, int otherDir)
00148 {
00149     return    bd.IsColor(p + cDir, c)
00150            && bd.IsColor(p + cDir + otherDir, opp)
00151            && bd.IsColor(p + cDir - otherDir, opp)
00152            && bd.IsEmpty(p + otherDir)
00153            && bd.IsEmpty(p - otherDir)
00154            ;
00155 }
00156 
00157 template<class BOARD>
00158 bool GoUctPatterns<BOARD>::CheckCut1(const GoBoard& bd, SgPoint p,
00159                                      SgBlackWhite c, int cDir, int otherDir)
00160 {
00161     SG_ASSERT_BW(c);
00162     return bd.IsColor(p + otherDir, c)
00163         && bd.IsColor(p + cDir + otherDir, SgOppBW(c));
00164 }
00165 
00166 template<class BOARD>
00167 bool GoUctPatterns<BOARD>::CheckCut2(const GoBoard& bd, SgPoint p,
00168                                      const SgBlackWhite c, int cDir,
00169                                      int otherDir)
00170 {
00171     SG_ASSERT_BW(c);
00172     SG_ASSERT(bd.IsColor(p + cDir, c));
00173     const SgBlackWhite opp = SgOppBW(c);
00174     return   bd.IsColor(p - cDir, c)
00175           && ( (  bd.IsColor(p + otherDir, opp)
00176                && ! bd.IsColor(p - otherDir + cDir, c)
00177                && ! bd.IsColor(p - otherDir - cDir, c)
00178                )
00179              ||
00180                (  bd.IsColor(p - otherDir, opp)
00181                && ! bd.IsColor(p + otherDir + cDir, c)
00182                && ! bd.IsColor(p + otherDir - cDir, c)
00183                )
00184              );
00185 }
00186 
00187 template<class BOARD>
00188 inline int GoUctPatterns<BOARD>::CodeOf8Neighbors(const BOARD& bd, SgPoint p)
00189 {
00190     SG_ASSERT(bd.Line(p) > 1);
00191     int code = (((((( EBWCodeOfPoint(bd, p - SG_NS - SG_WE) * 3
00192                     + EBWCodeOfPoint(bd, p - SG_NS)) * 3
00193                     + EBWCodeOfPoint(bd, p - SG_NS + SG_WE)) * 3
00194                     + EBWCodeOfPoint(bd, p - SG_WE)) * 3
00195                     + EBWCodeOfPoint(bd, p + SG_WE)) * 3
00196                     + EBWCodeOfPoint(bd, p + SG_NS - SG_WE)) * 3
00197                     + EBWCodeOfPoint(bd, p + SG_NS)) * 3
00198                     + EBWCodeOfPoint(bd, p + SG_NS + SG_WE);
00199     SG_ASSERT(code >= 0);
00200     SG_ASSERT(code < GOUCT_POWER3_8);
00201     return code;
00202 }
00203 
00204 template<class BOARD>
00205 inline int GoUctPatterns<BOARD>::CodeOfEdgeNeighbors(const BOARD& bd,
00206                                                      SgPoint p)
00207 {
00208     SG_ASSERT(bd.Line(p) == 1);
00209     SG_ASSERT(bd.Pos(p) > 1);
00210     const int up = bd.Up(p);
00211     const int other = OtherDir(up);
00212     int code = (((EBWCodeOfPoint(bd, p + other) * 3
00213                 + EBWCodeOfPoint(bd, p + up + other)) * 3
00214                 + EBWCodeOfPoint(bd, p + up)) * 3
00215                 + EBWCodeOfPoint(bd, p + up - other)) * 3
00216                 + EBWCodeOfPoint(bd, p - other);
00217     SG_ASSERT(code >= 0);
00218     SG_ASSERT(code < GOUCT_POWER3_5);
00219     return code;
00220 }
00221 
00222 template<class BOARD>
00223 inline int GoUctPatterns<BOARD>::EBWCodeOfPoint(const BOARD& bd, SgPoint p)
00224 {
00225     SG_ASSERT(bd.IsValidPoint(p));
00226     BOOST_STATIC_ASSERT(SG_BLACK == 0);
00227     BOOST_STATIC_ASSERT(SG_WHITE == 1);
00228     BOOST_STATIC_ASSERT(SG_EMPTY == 2);
00229     return bd.GetColor(p);
00230 }
00231 
00232 template<class BOARD>
00233 int GoUctPatterns<BOARD>::EdgeDirection(GoBoard& bd, SgPoint p, int index)
00234 {
00235     const int up = bd.Up(p);
00236     switch (index)
00237     {
00238         case 0:
00239             return OtherDir(up);
00240         case 1:
00241             return up + OtherDir(up);
00242         case 2:
00243             return up;
00244         case 3:
00245             return up - OtherDir(up);
00246         default:
00247             SG_ASSERT(index == 4);
00248             return - OtherDir(up);
00249     }
00250 }
00251 
00252 /** Find direction of a neighboring stone in color c */
00253 template<class BOARD>
00254 int GoUctPatterns<BOARD>::FindDir(const GoBoard& bd, SgPoint p,
00255                                   SgBlackWhite c)
00256 {
00257     if (bd.IsColor(p + SG_NS, c))
00258         return SG_NS;
00259     if (bd.IsColor(p - SG_NS, c))
00260         return -SG_NS;
00261     if (bd.IsColor(p + SG_WE, c))
00262         return SG_WE;
00263     SG_ASSERT(bd.IsColor(p - SG_WE, c));
00264     return -SG_WE;
00265 }
00266 
00267 template<class BOARD>
00268 void GoUctPatterns<BOARD>::InitEdgePatternTable(
00269                                   SgBWArray<GoUctEdgePatternTable>& edgeTable)
00270 {
00271     GoBoard bd(5);
00272     const SgPoint p = SgPointUtil::Pt(1, 3);
00273     for (int i = 0; i < GOUCT_POWER3_5; ++i)
00274     {
00275         int count = SetupCodedEdgePosition(bd, i);
00276         for(SgBWIterator it; it; ++it)
00277         {
00278             bd.SetToPlay(*it);
00279             edgeTable[*it][i] = MatchAnyPattern(bd, p) ? 1 : 0;
00280         }
00281         while (count-- > 0)
00282             bd.Undo();
00283     }
00284 }
00285 
00286 template<class BOARD>
00287 void GoUctPatterns<BOARD>::InitCenterPatternTable(
00288                                           SgBWArray<GoUctPatternTable>& table)
00289 {
00290     GoBoard bd(5);
00291     const SgPoint p = SgPointUtil::Pt(3, 3);
00292     for (int i = 0; i < GOUCT_POWER3_8; ++i)
00293     {
00294         int count = SetupCodedPosition(bd, i);
00295         for(SgBWIterator it; it; ++it)
00296         {
00297             bd.SetToPlay(*it);
00298             table[*it][i] = MatchAnyPattern(bd, p) ? 1 : 0;
00299         }
00300         while (count-- > 0)
00301             bd.Undo();
00302     }
00303 }
00304 
00305 template<class BOARD>
00306 bool GoUctPatterns<BOARD>::MatchCut(const GoBoard& bd, SgPoint p)
00307 {
00308     if (bd.Num8EmptyNeighbors(p) > 6)
00309         return false;
00310 
00311     const int nuEmpty = bd.NumEmptyNeighbors(p);
00312     //cut1
00313     const SgEmptyBlackWhite c1 = bd.GetColor(p + SG_NS);
00314     if (   c1 != SG_EMPTY
00315        && bd.NumNeighbors(p, c1) >= 2
00316        && ! (bd.NumNeighbors(p, c1) == 3 && nuEmpty == 1)
00317        && (  CheckCut1(bd, p, c1, SG_NS, SG_WE)
00318           || CheckCut1(bd, p, c1, SG_NS, -SG_WE)
00319           )
00320        )
00321         return true;
00322     const SgEmptyBlackWhite c2 = bd.GetColor(p - SG_NS);
00323     if (  c2 != SG_EMPTY
00324        && bd.NumNeighbors(p, c2) >= 2
00325        && ! (bd.NumNeighbors(p, c2) == 3 && nuEmpty == 1)
00326        && (  CheckCut1(bd, p, c2, -SG_NS, SG_WE)
00327           || CheckCut1(bd, p, c2, -SG_NS, -SG_WE)
00328           )
00329        )
00330         return true;
00331     //cut2
00332     if (  c1 != SG_EMPTY
00333        && bd.NumNeighbors(p, c1) == 2
00334        && bd.NumNeighbors(p, SgOppBW(c1)) > 0
00335        && bd.NumDiagonals(p, c1) <= 2
00336        && CheckCut2(bd, p, c1, SG_NS, SG_WE)
00337        )
00338         return true;
00339     const SgEmptyBlackWhite c3 = bd.GetColor(p + SG_WE);
00340     if (  c3 != SG_EMPTY
00341        && bd.NumNeighbors(p, c3) == 2
00342        && bd.NumNeighbors(p, SgOppBW(c3)) > 0
00343        && bd.NumDiagonals(p, c3) <= 2
00344        && CheckCut2(bd, p, c3, SG_WE, SG_NS)
00345        )
00346         return true;
00347     return false;
00348 }
00349 
00350 template<class BOARD>
00351 bool GoUctPatterns<BOARD>::MatchEdge(const GoBoard& bd, SgPoint p,
00352                                      const int nuBlack, const int nuWhite)
00353 {
00354     const int up = bd.Up(p);
00355     const int side = OtherDir(up);
00356     const int nuEmpty = bd.NumEmptyNeighbors(p);
00357     const SgEmptyBlackWhite upColor = bd.GetColor(p + up);
00358     // edge1
00359     if (  nuEmpty > 0
00360        && (nuBlack > 0 || nuWhite > 0)
00361        && upColor == SG_EMPTY
00362        )
00363     {
00364         const SgEmptyBlackWhite c1 = bd.GetColor(p + side);
00365         if (c1 != SG_EMPTY && bd.GetColor(p + side + up) == SgOppBW(c1))
00366             return true;
00367         const SgEmptyBlackWhite c2 = bd.GetColor(p - side);
00368         if (c2 != SG_EMPTY && bd.GetColor(p - side + up) == SgOppBW(c2))
00369             return true;
00370     }
00371 
00372     // edge2
00373     if (  upColor != SG_EMPTY
00374        && (  (upColor == SG_BLACK && nuBlack == 1 && nuWhite > 0)
00375           || (upColor == SG_WHITE && nuWhite == 1 && nuBlack > 0)
00376           )
00377        )
00378         return true;
00379 
00380     const SgBlackWhite toPlay = bd.ToPlay();
00381     // edge3
00382     if (  upColor == toPlay
00383        && bd.NumDiagonals(p, SgOppBW(upColor)) > 0
00384        )
00385         return true;
00386 
00387     // edge4
00388     if (  upColor == SgOppBW(toPlay)
00389        && bd.NumNeighbors(p, upColor) <= 2
00390        && bd.NumDiagonals(p, toPlay) > 0
00391        )
00392     {
00393         if (  bd.GetColor(p + side + up) == toPlay
00394            && bd.GetColor(p + side) != upColor
00395            )
00396             return true;
00397         if (  bd.GetColor(p - side + up) == toPlay
00398            && bd.GetColor(p - side) != upColor
00399            )
00400             return true;
00401     }
00402     // edge5
00403     if (  upColor == SgOppBW(toPlay)
00404        && bd.NumNeighbors(p, upColor) == 2
00405        && bd.NumNeighbors(p, toPlay) == 1
00406        )
00407     {
00408         if (  bd.GetColor(p + side + up) == toPlay
00409            && bd.GetColor(p + side) == upColor
00410            )
00411             return true;
00412         if (  bd.GetColor(p - side + up) == toPlay
00413            && bd.GetColor(p - side) == upColor
00414            )
00415             return true;
00416     }
00417     return false;
00418 }
00419 
00420 template<class BOARD>
00421 bool GoUctPatterns<BOARD>::MatchHane(const GoBoard& bd, SgPoint p,
00422                                      const int nuBlack, const int nuWhite)
00423 {
00424     const int nuEmpty = bd.NumEmptyNeighbors(p);
00425     if (nuEmpty < 2 || nuEmpty > 3)
00426         return false;
00427     if (  (nuBlack < 1 || nuBlack > 2)
00428        && (nuWhite < 1 || nuWhite > 2)
00429        )
00430         return false;
00431     if (nuEmpty == 2) // hane3 pattern
00432     {
00433         if (nuBlack == 1 && nuWhite == 1)
00434         {
00435             const int dirB = FindDir(bd, p, SG_BLACK);
00436             const int dirW = FindDir(bd, p, SG_WHITE);
00437             if (! bd.IsEmpty(p + dirB + dirW))
00438                 return true;
00439         }
00440     }
00441     else if (nuEmpty == 3) // hane2 or hane4
00442     {
00443         SG_ASSERT(nuBlack + nuWhite == 1);
00444         const SgBlackWhite col = (nuBlack == 1) ? SG_BLACK : SG_WHITE;
00445         const SgBlackWhite opp = SgOppBW(col);
00446         const int dir = FindDir(bd, p, col);
00447         const int otherDir = OtherDir(dir);
00448         if (  bd.IsEmpty(p + dir + otherDir)
00449            && bd.IsColor(p + dir - otherDir, opp)
00450            )
00451             return true; // hane2
00452         if (  bd.IsEmpty(p + dir - otherDir)
00453            && bd.IsColor(p + dir + otherDir, opp)
00454            )
00455             return true; // hane2
00456         if (bd.ToPlay() == opp)
00457         {
00458             const SgEmptyBlackWhite c1 = bd.GetColor(p + dir + otherDir);
00459             if (c1 != SG_EMPTY)
00460             {
00461                 const SgEmptyBlackWhite c2 =
00462                 bd.GetColor(p + dir - otherDir);
00463                 if (SgOppBW(c1) == c2)
00464                     return true; // hane4
00465             }
00466         }
00467     }
00468 
00469     // hane1 pattern
00470     const int nuBlackDiag = bd.NumDiagonals(p, SG_BLACK);
00471     if (  nuBlackDiag >= 2
00472        && nuWhite > 0
00473        && (  CheckHane1(bd, p, SG_WHITE, SG_BLACK, SG_NS, SG_WE)
00474           || CheckHane1(bd, p, SG_WHITE, SG_BLACK, -SG_NS, SG_WE)
00475           || CheckHane1(bd, p, SG_WHITE, SG_BLACK, SG_WE, SG_NS)
00476           || CheckHane1(bd, p, SG_WHITE, SG_BLACK, -SG_WE, SG_NS)
00477           )
00478        )
00479         return true;
00480     const int nuWhiteDiag = bd.NumDiagonals(p, SG_WHITE);
00481     if (  nuWhiteDiag >= 2
00482        && nuBlack > 0
00483        && (  CheckHane1(bd, p, SG_BLACK, SG_WHITE, SG_NS, SG_WE)
00484           || CheckHane1(bd, p, SG_BLACK, SG_WHITE, -SG_NS, SG_WE)
00485           || CheckHane1(bd, p, SG_BLACK, SG_WHITE, SG_WE, SG_NS)
00486           || CheckHane1(bd, p, SG_BLACK, SG_WHITE, -SG_WE, SG_NS)
00487           )
00488        )
00489         return true;
00490     return false;
00491 }
00492 
00493 template<class BOARD>
00494 inline bool GoUctPatterns<BOARD>::MatchAnyCenter(SgPoint p) const
00495 {
00496     return m_table[m_bd.ToPlay()][CodeOf8Neighbors(m_bd, p)];
00497 }
00498 
00499 template<class BOARD>
00500 inline bool GoUctPatterns<BOARD>::MatchAnyEdge(SgPoint p) const
00501 {
00502     return m_edgeTable[m_bd.ToPlay()][CodeOfEdgeNeighbors(m_bd, p)];
00503 }
00504 
00505 template<class BOARD>
00506 inline bool GoUctPatterns<BOARD>::MatchAny(SgPoint p) const
00507 {
00508     // Quick refutation using the incremental neighbor counts of the board:
00509     // all patterns have at least one adjacent stone
00510     if (  m_bd.NumNeighbors(p, SG_BLACK) == 0
00511        && m_bd.NumNeighbors(p, SG_WHITE) == 0
00512        )
00513         return false;
00514     if (m_bd.Line(p) > 1)
00515        return MatchAnyCenter(p);
00516     else if (m_bd.Pos(p) > 1)
00517         return MatchAnyEdge(p);
00518     else
00519         return false;
00520 }
00521 
00522 /** Procedural matching function - used to initialize the table. */
00523 template<class BOARD>
00524 bool GoUctPatterns<BOARD>::MatchAnyPattern(const GoBoard& bd, SgPoint p)
00525 {
00526     SG_ASSERT(bd.IsEmpty(p));
00527     const int nuBlack = bd.NumNeighbors(p, SG_BLACK);
00528     const int nuWhite = bd.NumNeighbors(p, SG_WHITE);
00529 
00530     // Quick refutation using the incremental neighbor counts of the board
00531     // All patterns have at least one adjacent stone
00532     if (nuBlack == 0 && nuWhite == 0)
00533         return false;
00534 
00535     // Filter edge moves on (1,1) points in corners
00536     if (bd.Pos(p) == 1)
00537         return false;
00538     if (bd.Line(p) == 1)
00539         return MatchEdge(bd, p, nuBlack, nuWhite);
00540     else // Center
00541         return   MatchHane(bd, p, nuBlack, nuWhite)
00542               || MatchCut(bd, p);
00543 }
00544 
00545 template<class BOARD>
00546 inline int GoUctPatterns<BOARD>::OtherDir(int dir)
00547 {
00548     if (dir == SG_NS || dir == -SG_NS)
00549         return SG_WE;
00550     return SG_NS;
00551 }
00552 
00553 template<class BOARD>
00554 int GoUctPatterns<BOARD>::SetupCodedEdgePosition(GoBoard& bd, int code)
00555 {
00556     const SgPoint p = SgPointUtil::Pt(1, 3);
00557     int count = 0;
00558     for (int i = 4; i >= 0; --i) // decoding gives points in reverse order
00559     {
00560         const SgPoint nb = p + EdgeDirection(bd, p, i);
00561         int c = code % 3;
00562         code /= 3;
00563         if (c != SG_EMPTY)
00564         {
00565             ++count;
00566             bd.Play(nb, c);
00567         }
00568     }
00569     return count;
00570 }
00571 
00572 template<class BOARD>
00573 int GoUctPatterns<BOARD>::SetupCodedPosition(GoBoard& bd, int code)
00574 {
00575     const SgPoint p = SgPointUtil::Pt(3, 3);
00576     int count = 0;
00577     for (int i = 7; i >= 0; --i) // decoding gives points in reverse order
00578     {
00579         const SgPoint nb = p + SgNb8Iterator::Direction(i);
00580         int c = code % 3;
00581         code /= 3;
00582         if (c != SG_EMPTY)
00583         {
00584             ++count;
00585             bd.Play(nb, c);
00586         }
00587     }
00588     return count;
00589 }
00590 
00591 //----------------------------------------------------------------------------
00592 
00593 #endif // GOUCT_PATTERNS_H


Sun Mar 13 2011 Doxygen 1.7.1